[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-microsoft-audit-permissions":3,"mdc--ygehbt-key":43,"related-repo-microsoft-audit-permissions":5243,"related-org-microsoft-audit-permissions":5342},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":38,"sourceUrl":41,"mdContent":42},"audit-permissions","audit Power Pages table permissions","Audits existing table permissions on a Power Pages site by analyzing them against site code and Dataverse metadata. Generates an HTML audit report with findings grouped by severity (critical, warning, info, pass) and suggests fixes for issues found. Use when the user wants to review, verify, or check table permissions for security issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"microsoft","Microsoft","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fmicrosoft.png",[12,16,19,22,23],{"name":13,"slug":14,"type":15},"Security","security","tag",{"name":17,"slug":18,"type":15},"Audit","audit",{"name":20,"slug":21,"type":15},"Power Pages","power-pages",{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},"Access Control","access-control",564,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills","2026-04-06T18:34:37.352115",null,114,[32,33,34,35,36,21,37],"claude-code-plugin","claude-code-skills","code-apps","github-copilot-plugin","power-apps","power-platform",{"repoUrl":27,"stars":26,"forks":30,"topics":39,"description":40},[32,33,34,35,36,21,37],"A plugin marketplace for Claude Code\u002FGitHub Copilot that provides Power Platform development plugins, including reusable skills, agents, and commands for building and deploying solutions.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Ftree\u002FHEAD\u002Fplugins\u002Fpower-pages\u002Fskills\u002Faudit-permissions","---\nname: audit-permissions\ndescription: >-\n  Audits existing table permissions on a Power Pages site by analyzing them against site code\n  and Dataverse metadata. Generates an HTML audit report with findings grouped by severity\n  (critical, warning, info, pass) and suggests fixes for issues found. Use when the user wants\n  to review, verify, or check table permissions for security issues.\nuser-invocable: true\nargument-hint: \"[optional: specific table or concern]\"\nallowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion, TaskCreate, TaskUpdate, TaskList, Agent\nmodel: opus\n---\n\n> **Plugin check**: Run `node \"${PLUGIN_ROOT}\u002Fscripts\u002Fcheck-version.js\"` — if it outputs a message, show it to the user before proceeding.\n\n# Audit Permissions\n\nAudit 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.\n\n## Workflow\n\n1. **Verify Site Deployment** — Check that `.powerpages-site` folder and table permissions exist\n2. **Gather Configuration** — Read all web roles, table permissions, and site code\n3. **Run Local Schema Validation** — Use the shared validator to detect invalid permission\u002Fsite-setting YAML before deeper analysis\n4. **Analyze & Discover** — Query Dataverse for relationships and lookup columns using deterministic scripts\n5. **Run Audit Checks** — Compare permissions against code usage and best practices\n6. **Generate Report** — Create the HTML audit report and display in browser\n7. **Present Findings & Track** — Summarize findings, record skill usage, and ask user if they want to fix issues\n\n**Important:** Do NOT ask the user questions during analysis. Autonomously gather all data, then present findings.\n\n## Task Tracking\n\nAt the start of Step 1, create all tasks upfront using `TaskCreate`. Mark each task `in_progress` when starting and `completed` when done.\n\n| Task subject | activeForm | Description |\n|-------------|------------|-------------|\n| Verify site deployment | Verifying site deployment | Check .powerpages-site folder and table permissions exist |\n| Gather configuration | Gathering configuration | Read web roles, table permissions, and site code |\n| Run local schema validation | Validating local permissions schema | Run shared validator against existing table permission and site setting YAML |\n| Discover relationships | Discovering relationships | Query Dataverse for lookup columns and relationships |\n| Run audit checks | Running audit checks | Create per-table tasks and run checklist (A–K) for each table, then cross-validate |\n| Generate audit report | Generating audit report | Create HTML report and display in browser |\n| Present findings | Presenting findings | Summarize results, record usage, and offer to fix issues |\n\n**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.\n\n---\n\n## Step 1: Verify Site Deployment\n\nUse `Glob` to find:\n\n- `**\u002Fpowerpages.config.json` — identifies the project root\n- `**\u002F.powerpages-site\u002Ftable-permissions\u002F*.tablepermission.yml` — existing permissions\n\nIf no `.powerpages-site` folder exists, stop and tell the user to deploy first using `\u002Fdeploy-site`.\nIf 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.\n\n---\n\n## Step 2: Gather Configuration\n\n### 2.1 Read Web Roles\n\nRead all files matching `**\u002F.powerpages-site\u002Fweb-roles\u002F*.yml`. Extract `id`, `name`, `anonymoususersrole`, `authenticatedusersrole` from each.\n\n### 2.2 Read Table Permissions\n\nRead all files matching `**\u002F.powerpages-site\u002Ftable-permissions\u002F*.tablepermission.yml`. For each permission, extract:\n\n- `entityname` (permission name)\n- `entitylogicalname` (table)\n- `scope` (numeric code)\n- `read`, `create`, `write`, `delete`, `append`, `appendto` (boolean flags)\n- `adx_entitypermission_webrole` (array of web role UUIDs)\n- `contactrelationship`, `accountrelationship` (if Contact\u002FAccount scope)\n- `parententitypermission`, `parentrelationship` (if parent scope)\n\n### 2.3 Analyze Site Code\n\nSearch the site source code for:\n\n- Web API calls (`\u002F_api\u002F`)\n- Lookup bindings (`@odata.bind`)\n- File uploads (`uploadFileColumn`, `uploadFile`, `upload*Photo`, `upload*Image`)\n- `$expand` usage (`$expand`, `buildExpandClause`, `ExpandOption`)\n\nAlso check for `.datamodel-manifest.json` in the project root for the authoritative table list.\n\nBuild 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).\n\n### 2.4 Run Shared Schema Validator\n\nRun the shared validator against the existing site:\n\n```bash\nnode \"${PLUGIN_ROOT}\u002Fscripts\u002Fvalidate-permissions-schema.js\" --projectRoot \"\u003CPROJECT_ROOT>\"\n```\n\nParse the JSON output and carry the findings into the audit. Treat:\n\n- `error` findings as **critical**\n- `warning` findings as **warning**\n- `info` findings as **info**\n\nThese findings should be included in the final audit report even if the later code\u002FDataverse analysis also finds additional issues.\n\nAfter Step 3.1 determines the environment URL, if this audit is running locally with Dataverse access available, rerun the shared validator with live relationship verification enabled and merge any additional findings:\n\n```bash\nnode \"${PLUGIN_ROOT}\u002Fscripts\u002Fvalidate-permissions-schema.js\" --projectRoot \"\u003CPROJECT_ROOT>\" --validate-dataverse-relationships --envUrl \"\u003CenvUrl>\"\n```\n\nUse this Dataverse-backed relationship validation only for local runs. Do **not** require it in CI or other offline contexts.\n\n---\n\n## Step 3: Analyze & Discover (Dataverse API)\n\nUse deterministic Node.js scripts for all Dataverse API calls. These scripts handle auth token acquisition, HTTP requests, and JSON parsing consistently.\n\n### 3.1 Get Environment URL\n\n```bash\npac env who\n```\n\nExtract the `Environment URL` (e.g., `https:\u002F\u002Forg12345.crm.dynamics.com`) and use it as `\u003CenvUrl>` in subsequent script calls.\n\n### 3.2 Query Lookup Columns\n\nFor each table that has permissions with `create` or `write` enabled, use the lookup query script:\n\n```bash\nnode \"${PLUGIN_ROOT}\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-lookups.js\" --envUrl \"\u003CenvUrl>\" --table \"\u003Ctable_logical_name>\"\n```\n\nThe script returns a JSON array of `{ logicalName, targets }` for each lookup column. Capture this output for the maps described below.\n\nAfter querying **all** tables with create or write permissions, build two maps from the combined results:\n\n1. **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.\n2. **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.\n\nExample: querying `order_item` returns `[{ logicalName: \"cr4fc_orderid\", targets: [\"cr4fc_order\"] }]`\n- Source map: `order_item → [{ column: \"cr4fc_orderid\", targets: [\"cr4fc_order\"] }]`\n- Reverse target map: `cr4fc_order → [{ sourceTable: \"order_item\", column: \"cr4fc_orderid\" }]`\n\nBoth maps are used in Sections H and H2:\n- The **source table** (with the lookup) needs `appendto: true` — it links TO other records (checked via the source map)\n- Each **target table** in `targets` needs `append: true` — other records link TO it (checked via the reverse target map)\n\n### 3.3 Query Relationships\n\nFor tables with parent-scope permissions, verify the relationship names using the relationship query script:\n\n```bash\nnode \"${PLUGIN_ROOT}\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-relationships.js\" --envUrl \"\u003CenvUrl>\" --table \"\u003Cparent_table>\"\n```\n\nThe script returns a JSON array of `{ schemaName, referencedEntity, referencingEntity, referencingAttribute }`. Use `schemaName` to validate the `parentrelationship` value in parent-scope permissions.\n\n### Error Handling\n\nIf 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.\n\n---\n\n## Step 4: Run Audit Checks\n\nUse 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`.\n\n### 4.1 Build Audit Inventory\n\nFirst, build a combined list of all tables to audit from two sources:\n\n1. **Tables referenced in code** (from Step 2.3) — these may or may not have permissions\n2. **Tables with existing permissions** (from Step 2.2) — these may or may not be referenced in code\n\nThe 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?\"\n\n### 4.2 Create Per-Table Audit Tasks\n\nFor each table in the audit inventory, create a task:\n\n```\nTaskCreate:\n  subject: \"Audit \u003Ctable_logical_name>\"\n  activeForm: \"Auditing \u003Ctable_display_name> permissions\"\n  description: \"Run all audit checks for \u003Ctable_logical_name>\"\n```\n\nAlso create a summary task:\n\n```\nTaskCreate:\n  subject: \"Compile audit findings\"\n  activeForm: \"Compiling audit findings\"\n  description: \"Combine all per-table findings into the final report\"\n```\n\nUse `TaskList` at any point to review progress and see which tables still need auditing.\n\n### 4.3 Per-Table Audit Checklist\n\nFor 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.\n\n**A. Permission Existence**\n\nDoes this table have a table permission?\n\n- If the table is referenced in code but has **no permission** → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing permission for \u003Ctable>`\n  - **Reasoning:** Which code files reference this table and what operations they perform\n  - **Fix:** Create a permission with the appropriate scope and CRUD flags\n- If a permission exists but the table is **not referenced in code** → finding:\n  - **Severity:** `info`\n  - **Title:** `Unused permission for \u003Ctable>`\n  - **Reasoning:** The table is not referenced in any source code — the permission may be unnecessary\n  - **Fix:** Review whether this permission is still needed\n- If both exist → `pass`, proceed to remaining checks\n\n**B. Web Role Association**\n\nDoes the permission have web role(s) assigned?\n\n- Check `adx_entitypermission_webrole` — if empty or missing → finding:\n  - **Severity:** `warning`\n  - **Title:** `Permission \u003Cname> has no web role association`\n  - **Reasoning:** A permission without a web role has no effect — no users will receive this access\n  - **Fix:** Associate with the appropriate web role\n- If roles are assigned → `pass`\n\n**C. Scope Appropriateness**\n\nIs the scope the least-privileged option that fits?\n\n- Search the service code for scope-relevant patterns: contact-scoped filters (`getCurrentContactId`, `_contactid_value`, `contactid`) and account-scoped filters (`_accountid_value`, `parentcustomerid`)\n- If Global scope (`756150000`) with `write` or `delete` enabled → finding:\n  - **Severity:** `warning`\n  - **Title:** `Global scope with write\u002Fdelete on \u003Ctable>`\n  - **Reasoning:** Any user with this role can modify\u002Fdelete any record in this table\n  - **Fix:** Narrow to Contact or Account scope, or remove write\u002Fdelete if not needed\n- If Global scope with only `read` → `pass` (acceptable for public reference data)\n- If code uses contact-scoped filters but permission uses Global → finding:\n  - **Severity:** `warning`\n  - **Title:** `Scope could be narrower for \u003Ctable>`\n  - **Reasoning:** Code filters by current contact but permission grants Global access\n  - **Fix:** Narrow to Contact scope\n- Otherwise → `pass`\n\n**D. Read Permission**\n\nIs `read` correctly set?\n\n- Search the service code for GET\u002Flist\u002Fget patterns for this table: API calls to `\u002F_api\u002F\u003Centity_set>`, list\u002Fget functions (`list\u003CTableName>`, `get\u003CTableName>`)\n- If code reads this table but `read: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing read permission for \u003Ctable>`\n  - **Reasoning:** Code reads from this table but permission does not grant read access\n  - **Fix:** Enable `read: true`\n- If `read: true` and code reads → `pass`\n\n**E. Create Permission**\n\nIs `create` correctly set?\n\n- Search the service code for POST\u002Fcreate patterns: POST method usage (`method: 'POST'`), create functions (`create\u003CTableName>`)\n- If code creates records but `create: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing create permission for \u003Ctable>`\n  - **Reasoning:** Code creates records in this table but permission does not grant create access\n  - **Fix:** Enable `create: true`\n- If `create: true` but no create patterns in code → finding:\n  - **Severity:** `info`\n  - **Title:** `Create enabled but not used for \u003Ctable>`\n  - **Reasoning:** No create operations found in code — permission may be overly permissive\n  - **Fix:** Consider disabling `create` if not needed\n- If matched → `pass`\n\n**F. Write Permission**\n\nIs `write` correctly set?\n\n- Search the service code for PATCH\u002Fupdate\u002Fupload patterns: PATCH method usage (`method: 'PATCH'`), update functions (`update\u003CTableName>`), file upload patterns (`uploadFileColumn`, `uploadFile`, `upload*Photo`, `upload*Image`, `upload*File`)\n- If code updates records but `write: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing write permission for \u003Ctable>`\n  - **Reasoning:** Code updates records (or uploads files) in this table but permission does not grant write access\n  - **Fix:** Enable `write: true`\n- If file upload patterns found but `write: false` → finding:\n  - **Severity:** `warning`\n  - **Title:** `File upload detected but write is disabled on \u003Ctable>`\n  - **Reasoning:** File uploads use PATCH which requires write permission\n  - **Fix:** Enable `write: true`\n- If `write: true` but `read: false` → finding:\n  - **Severity:** `warning`\n  - **Title:** `Write enabled without read on \u003Ctable>`\n  - **Reasoning:** Users can modify records they cannot see, which is unusual and likely unintended\n  - **Fix:** Enable `read: true`\n- If `write: true` but no write patterns in code → finding:\n  - **Severity:** `info`\n  - **Title:** `Write enabled but not used for \u003Ctable>`\n  - **Reasoning:** No update operations found in code — permission may be overly permissive\n  - **Fix:** Consider disabling `write` if not needed\n- If matched → `pass`\n\n**G. Delete Permission**\n\nIs `delete` correctly set?\n\n- Search the service code for DELETE patterns: DELETE method usage (`method: 'DELETE'`), delete functions (`delete\u003CTableName>`)\n- If code deletes records but `delete: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing delete permission for \u003Ctable>`\n  - **Reasoning:** Code deletes records in this table but permission does not grant delete access\n  - **Fix:** Enable `delete: true`\n- If `delete: true` but no delete patterns in code → finding:\n  - **Severity:** `info`\n  - **Title:** `Delete enabled but not used for \u003Ctable>`\n  - **Reasoning:** No delete operations found in code — permission may be overly permissive\n  - **Fix:** Consider disabling `delete` if not needed\n- If matched → `pass`\n\n**H. Append (target table check)**\n\nDoes this table need `append: true`? Append is required on the **target** table — the table that other records link TO via lookup columns.\n\n- 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?\n- Also search the service code for `@odata.bind` references to this table's entity set (e.g., `\u002F\u003Centity_set>(`)\n- If this table appears in the reverse target map (i.e., another table with create\u002Fwrite has a lookup to this table), but `append: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing append on \u003Ctable>`\n  - **Reasoning:** Table `\u003Csource_table>` has lookup column `\u003Ccolumn>` targeting this table and sets it during create\u002Fwrite. 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\"\n  - **Fix:** Enable `append: true`\n- If `append: true` and justified → `pass`\n- If `append: true` but this table does NOT appear in the reverse target map and no code references it as a lookup target → finding:\n  - **Severity:** `info`\n  - **Title:** `Append enabled but not needed on \u003Ctable>`\n  - **Reasoning:** No other table with create\u002Fwrite has a lookup to this table — append may be unnecessary\n  - **Fix:** Consider disabling `append` if not needed\n\n**H2. AppendTo (source table check)**\n\nDoes this table need `appendto: true`? AppendTo is required on the **source** table — the table that has lookup columns linking TO other records.\n\n- Check the **source map** from Step 3.2: does this table have lookup columns?\n- Also search the service code for `@odata.bind` patterns in create\u002Fupdate calls for this table\n- If this table has lookup columns (in source map or code) AND has `create` or `write` enabled, but `appendto: false` → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing appendto on \u003Ctable>`\n  - **Reasoning:** This table sets lookup column `\u003Ccolumn>` targeting `\u003Ctarget_table>` during create\u002Fwrite, which requires appendto permission. Users will see \"You don't have permission to associate or disassociate\"\n  - **Fix:** Enable `appendto: true`\n- If `appendto: true` and justified → `pass`\n\n**I. Parent Chain Integrity**\n\nIf the permission has Parent scope (`756150003`):\n\n- Verify `parententitypermission` references a valid permission ID that exists\n- Verify `parentrelationship` is a valid Dataverse relationship (if API available, using Step 3.3 results)\n- If broken → finding:\n  - **Severity:** `critical`\n  - **Title:** `Broken parent chain for \u003Cpermission>`\n  - **Reasoning:** The parent permission reference is invalid — this permission will not grant any access\n  - **Fix:** Correct the parent permission ID and\u002For relationship name\n- If valid → `pass`\n\n**J. $expand Related Table Coverage**\n\nIs this table fetched via `$expand` on another table's query?\n\n- Check the `$expand` analysis from Step 2.3 (search site source code for `$expand`, `buildExpandClause`, `ExpandOption`)\n- If this table is expanded from another table but has no table permission with `read: true` for the same web role → finding:\n  - **Severity:** `critical`\n  - **Title:** `Missing read permission for expanded table \u003Ctable>`\n  - **Reasoning:** This table is fetched via `$expand` on `\u003Cparent_table>` in `\u003Cservice_file>`, but has no read permission. Power Pages enforces table permissions on every entity in the query.\n  - **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.\n- If properly covered → `pass`\n\n**K. Record Findings & Complete**\n\nAfter all checks, mark the table's task as `completed` via `TaskUpdate`.\n\n### 4.4 Cross-Table Validation\n\nAfter all per-table audits are complete, run these cross-table checks:\n\n1. **Append\u002FAppendTo consistency:** Using the source map and reverse target map from Step 3.2, verify: (a) every source table (with lookups and create\u002Fwrite) 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\n2. **$expand coverage:** For every `$expand` usage, verify the expanded table has `read: true`\n3. **Parent chain completeness:** For every Parent scope permission, verify the parent permission exists and is valid\n4. **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\n\nUse `TaskList` to review all completed audits, then mark the \"Compile audit findings\" task as `in_progress` and proceed to Step 5.\n\n---\n\n## Step 5: Generate Report\n\n### 5.1 Determine Output Location\n\n- **If working in context of a website** (project root with `powerpages.config.json` exists): write to `\u003CPROJECT_ROOT>\u002Fdocs\u002Fpermissions-audit.html`\n- **Otherwise**: write to the system temp directory\n\n### 5.2 Prepare Data\n\n**Do NOT generate HTML manually or read\u002Fmodify the template yourself.** Use the `render-audit-report.js` script which mechanically reads the template and replaces placeholder tokens with your data.\n\nWrite a temporary JSON data file (e.g., `\u003COUTPUT_DIR>\u002Faudit-data.json`) with these keys:\n\n```json\n{\n  \"SITE_NAME\": \"The site name (from powerpages.config.json or folder name)\",\n  \"AUDIT_DESC\": \"Security audit of table permissions for Contoso Portal\",\n  \"SUMMARY\": \"2-3 sentence summary of the audit results\",\n  \"FINDINGS_DATA\": [\u002F* array of finding objects *\u002F],\n  \"INVENTORY_DATA\": [\u002F* array of current permission objects *\u002F]\n}\n```\n\n**FINDINGS_DATA format:**\n\n```json\n{\n  \"id\": \"f1\",\n  \"severity\": \"critical\",\n  \"title\": \"Missing permission for cra5b_product\",\n  \"table\": \"cra5b_product\",\n  \"scope\": null,\n  \"permission\": null,\n  \"reasoning\": \"The table cra5b_product is referenced in src\u002Fservices\u002FproductService.ts with GET requests to \u002F_api\u002Fcra5b_products, but no table permission exists for this table.\",\n  \"fix\": \"Create a table permission with Global scope and read-only access for the Anonymous Users role.\",\n  \"details\": \"Referenced in: src\u002Fservices\u002FproductService.ts (line 23), src\u002Fcomponents\u002FProductList.tsx (line 45)\"\n}\n```\n\n- `severity`: One of `critical`, `warning`, `info`, `pass`\n- `table`: The table logical name this finding relates to (or `null` for general findings)\n- `scope`: The current scope if applicable (numeric code or friendly name), or `null`\n- `permission`: The permission name if this finding is about an existing permission, or `null`\n- `reasoning`: Detailed explanation of why this is an issue — reference specific code files, line patterns, or Dataverse metadata\n- `fix`: Actionable suggestion for how to resolve the issue (or `null` for `pass` findings)\n- `details`: Additional context like file references, column names, or relationship details\n\n**INVENTORY_DATA format:**\n\n```json\n{\n  \"name\": \"Product - Anonymous Read\",\n  \"table\": \"cra5b_product\",\n  \"scope\": \"Global\",\n  \"roles\": [\"Anonymous Users\"],\n  \"read\": true,\n  \"create\": false,\n  \"write\": false,\n  \"delete\": false,\n  \"append\": true,\n  \"appendto\": false\n}\n```\n\n### 5.3 Render the HTML File\n\nRun the render script (it creates the output directory if needed):\n\n```bash\nnode \"${PLUGIN_ROOT}\u002Fscripts\u002Frender-audit-report.js\" --output \"\u003COUTPUT_PATH>\" --data \"\u003CDATA_JSON_PATH>\"\n```\n\nThe render script refuses to overwrite existing files. Before calling it, check if the default output path (`\u003CPROJECT_ROOT>\u002Fdocs\u002Fpermissions-audit.html`) already exists. If it does, choose a new descriptive filename based on context — e.g., `permissions-audit-apr-2026.html`, `permissions-audit-post-migration.html`. Pass the chosen name via `--output`.\n\nDelete the temporary data JSON file after the script succeeds.\n\n### 5.4 Open in Browser\n\nOpen the actual output path in the user's default browser.\n\n---\n\n## Step 6: Present Findings & Track\n\n### 6.1 Record Skill Usage\n\n> Reference: `${PLUGIN_ROOT}\u002Freferences\u002Fskill-tracking-reference.md`\n\nFollow the skill tracking instructions in the reference to record this skill's usage. Use `--skillName \"AuditPermissions\"`.\n\n### 6.2 Present Summary\n\n\u003C!-- gate: audit-permissions:6.fix-offer | category=plan | cancel-leaves=nothing -->\n\n> 🚦 **Gate (plan · audit-permissions:6.fix-offer):** Offer to apply auto-fixes for critical\u002Fwarning findings. The audit report has already been written; declining here just leaves the HTML report in place — no Dataverse \u002F filesystem mutation.\n>\n> **Trigger:** Phase 6 has tallied findings and the HTML report is saved.\n> **Why we ask:** Tooling could silently invoke the table-permissions-architect agent — accept-by-default would write or mutate permission YAML against the user's intent.\n> **Cancel leaves:** Nothing — the audit report stays at its saved path. No web-role \u002F table-permission files written.\n\nPresent a summary to the user:\n\n1. **Critical findings count** — these need immediate attention\n2. **Warning findings count** — should be addressed\n3. **Report location** — where the HTML file was saved\n4. **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.\"\n\nIf the user wants fixes applied:\n\n- **For 403 \u002F Web API access issues** (missing table permissions, missing CRUD flags, incorrect scope, missing append\u002Fappendto, 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: \u003Ctable1> needs Global scope with read:true; \u003Ctable2> needs Parent scope under \u003Cparent_table> with read:true, create:true, append:true; \u003Ctable3> needs appendto:true for lookups from \u003Csource_table>. The site project root is \u003CPROJECT_ROOT>.\"`\n- **For non-permission issues** (e.g., unused permissions, scope narrowing suggestions, informational findings): explain what manual changes are needed or suggest running `\u002Fintegrate-webapi` so the Web API settings architect can address site-setting-level issues.\n\n---\n\n## Critical Constraints\n\n- **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.\n- **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.\n- **No questions during analysis**: Autonomously gather all data, run checks, and present findings. Only ask the user at the end about fixing issues.\n- **Security**: Never log or display auth tokens. The scripts handle token acquisition internally via `getAuthToken()`.\n- **Graceful degradation**: If Dataverse API scripts fail (exit code 1), skip API-dependent checks (H\u002FH2 append\u002Fappendto validation, I parent chain integrity) and note in the report which checks were skipped.\n",{"data":44,"body":49},{"name":4,"description":6,"user-invocable":45,"argument-hint":46,"allowed-tools":47,"model":48},true,"[optional: specific table or concern]","Read, Write, Bash, Glob, Grep, AskUserQuestion, TaskCreate, TaskUpdate, TaskList, Agent","opus",{"type":50,"children":51},"root",[52,79,85,90,97,180,190,196,225,384,401,405,411,424,450,470,473,479,486,529,535,546,675,681,686,778,791,803,809,814,885,890,937,942,947,1020,1032,1035,1041,1046,1052,1077,1105,1111,1130,1199,1212,1224,1269,1288,1313,1318,1368,1374,1379,1447,1474,1480,1485,1488,1494,1526,1532,1537,1560,1565,1571,1576,1586,1591,1600,1612,1618,1644,1652,1657,1796,1804,1809,1882,1890,1895,2095,2103,2115,2228,2236,2246,2410,2418,2428,2753,2761,2771,2932,2940,2959,3166,3174,3192,3324,3332,3345,3434,3442,3454,3584,3592,3611,3617,3622,3705,3723,3726,3732,3738,3775,3781,3799,3812,4028,4036,4361,4489,4497,4808,4814,4819,4890,4924,4929,4935,4940,4943,4949,4955,4969,4981,4987,5026,5031,5082,5087,5146,5149,5155,5237],{"type":53,"tag":54,"props":55,"children":56},"element","blockquote",{},[57],{"type":53,"tag":58,"props":59,"children":60},"p",{},[61,68,70,77],{"type":53,"tag":62,"props":63,"children":64},"strong",{},[65],{"type":66,"value":67},"text","Plugin check",{"type":66,"value":69},": Run ",{"type":53,"tag":71,"props":72,"children":74},"code",{"className":73},[],[75],{"type":66,"value":76},"node \"${PLUGIN_ROOT}\u002Fscripts\u002Fcheck-version.js\"",{"type":66,"value":78}," — if it outputs a message, show it to the user before proceeding.",{"type":53,"tag":80,"props":81,"children":82},"h1",{"id":4},[83],{"type":66,"value":84},"Audit Permissions",{"type":53,"tag":58,"props":86,"children":87},{},[88],{"type":66,"value":89},"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.",{"type":53,"tag":91,"props":92,"children":94},"h2",{"id":93},"workflow",[95],{"type":66,"value":96},"Workflow",{"type":53,"tag":98,"props":99,"children":100},"ol",{},[101,120,130,140,150,160,170],{"type":53,"tag":102,"props":103,"children":104},"li",{},[105,110,112,118],{"type":53,"tag":62,"props":106,"children":107},{},[108],{"type":66,"value":109},"Verify Site Deployment",{"type":66,"value":111}," — Check that ",{"type":53,"tag":71,"props":113,"children":115},{"className":114},[],[116],{"type":66,"value":117},".powerpages-site",{"type":66,"value":119}," folder and table permissions exist",{"type":53,"tag":102,"props":121,"children":122},{},[123,128],{"type":53,"tag":62,"props":124,"children":125},{},[126],{"type":66,"value":127},"Gather Configuration",{"type":66,"value":129}," — Read all web roles, table permissions, and site code",{"type":53,"tag":102,"props":131,"children":132},{},[133,138],{"type":53,"tag":62,"props":134,"children":135},{},[136],{"type":66,"value":137},"Run Local Schema Validation",{"type":66,"value":139}," — Use the shared validator to detect invalid permission\u002Fsite-setting YAML before deeper analysis",{"type":53,"tag":102,"props":141,"children":142},{},[143,148],{"type":53,"tag":62,"props":144,"children":145},{},[146],{"type":66,"value":147},"Analyze & Discover",{"type":66,"value":149}," — Query Dataverse for relationships and lookup columns using deterministic scripts",{"type":53,"tag":102,"props":151,"children":152},{},[153,158],{"type":53,"tag":62,"props":154,"children":155},{},[156],{"type":66,"value":157},"Run Audit Checks",{"type":66,"value":159}," — Compare permissions against code usage and best practices",{"type":53,"tag":102,"props":161,"children":162},{},[163,168],{"type":53,"tag":62,"props":164,"children":165},{},[166],{"type":66,"value":167},"Generate Report",{"type":66,"value":169}," — Create the HTML audit report and display in browser",{"type":53,"tag":102,"props":171,"children":172},{},[173,178],{"type":53,"tag":62,"props":174,"children":175},{},[176],{"type":66,"value":177},"Present Findings & Track",{"type":66,"value":179}," — Summarize findings, record skill usage, and ask user if they want to fix issues",{"type":53,"tag":58,"props":181,"children":182},{},[183,188],{"type":53,"tag":62,"props":184,"children":185},{},[186],{"type":66,"value":187},"Important:",{"type":66,"value":189}," Do NOT ask the user questions during analysis. Autonomously gather all data, then present findings.",{"type":53,"tag":91,"props":191,"children":193},{"id":192},"task-tracking",[194],{"type":66,"value":195},"Task Tracking",{"type":53,"tag":58,"props":197,"children":198},{},[199,201,207,209,215,217,223],{"type":66,"value":200},"At the start of Step 1, create all tasks upfront using ",{"type":53,"tag":71,"props":202,"children":204},{"className":203},[],[205],{"type":66,"value":206},"TaskCreate",{"type":66,"value":208},". Mark each task ",{"type":53,"tag":71,"props":210,"children":212},{"className":211},[],[213],{"type":66,"value":214},"in_progress",{"type":66,"value":216}," when starting and ",{"type":53,"tag":71,"props":218,"children":220},{"className":219},[],[221],{"type":66,"value":222},"completed",{"type":66,"value":224}," when done.",{"type":53,"tag":226,"props":227,"children":228},"table",{},[229,253],{"type":53,"tag":230,"props":231,"children":232},"thead",{},[233],{"type":53,"tag":234,"props":235,"children":236},"tr",{},[237,243,248],{"type":53,"tag":238,"props":239,"children":240},"th",{},[241],{"type":66,"value":242},"Task subject",{"type":53,"tag":238,"props":244,"children":245},{},[246],{"type":66,"value":247},"activeForm",{"type":53,"tag":238,"props":249,"children":250},{},[251],{"type":66,"value":252},"Description",{"type":53,"tag":254,"props":255,"children":256},"tbody",{},[257,276,294,312,330,348,366],{"type":53,"tag":234,"props":258,"children":259},{},[260,266,271],{"type":53,"tag":261,"props":262,"children":263},"td",{},[264],{"type":66,"value":265},"Verify site deployment",{"type":53,"tag":261,"props":267,"children":268},{},[269],{"type":66,"value":270},"Verifying site deployment",{"type":53,"tag":261,"props":272,"children":273},{},[274],{"type":66,"value":275},"Check .powerpages-site folder and table permissions exist",{"type":53,"tag":234,"props":277,"children":278},{},[279,284,289],{"type":53,"tag":261,"props":280,"children":281},{},[282],{"type":66,"value":283},"Gather configuration",{"type":53,"tag":261,"props":285,"children":286},{},[287],{"type":66,"value":288},"Gathering configuration",{"type":53,"tag":261,"props":290,"children":291},{},[292],{"type":66,"value":293},"Read web roles, table permissions, and site code",{"type":53,"tag":234,"props":295,"children":296},{},[297,302,307],{"type":53,"tag":261,"props":298,"children":299},{},[300],{"type":66,"value":301},"Run local schema validation",{"type":53,"tag":261,"props":303,"children":304},{},[305],{"type":66,"value":306},"Validating local permissions schema",{"type":53,"tag":261,"props":308,"children":309},{},[310],{"type":66,"value":311},"Run shared validator against existing table permission and site setting YAML",{"type":53,"tag":234,"props":313,"children":314},{},[315,320,325],{"type":53,"tag":261,"props":316,"children":317},{},[318],{"type":66,"value":319},"Discover relationships",{"type":53,"tag":261,"props":321,"children":322},{},[323],{"type":66,"value":324},"Discovering relationships",{"type":53,"tag":261,"props":326,"children":327},{},[328],{"type":66,"value":329},"Query Dataverse for lookup columns and relationships",{"type":53,"tag":234,"props":331,"children":332},{},[333,338,343],{"type":53,"tag":261,"props":334,"children":335},{},[336],{"type":66,"value":337},"Run audit checks",{"type":53,"tag":261,"props":339,"children":340},{},[341],{"type":66,"value":342},"Running audit checks",{"type":53,"tag":261,"props":344,"children":345},{},[346],{"type":66,"value":347},"Create per-table tasks and run checklist (A–K) for each table, then cross-validate",{"type":53,"tag":234,"props":349,"children":350},{},[351,356,361],{"type":53,"tag":261,"props":352,"children":353},{},[354],{"type":66,"value":355},"Generate audit report",{"type":53,"tag":261,"props":357,"children":358},{},[359],{"type":66,"value":360},"Generating audit report",{"type":53,"tag":261,"props":362,"children":363},{},[364],{"type":66,"value":365},"Create HTML report and display in browser",{"type":53,"tag":234,"props":367,"children":368},{},[369,374,379],{"type":53,"tag":261,"props":370,"children":371},{},[372],{"type":66,"value":373},"Present findings",{"type":53,"tag":261,"props":375,"children":376},{},[377],{"type":66,"value":378},"Presenting findings",{"type":53,"tag":261,"props":380,"children":381},{},[382],{"type":66,"value":383},"Summarize results, record usage, and offer to fix issues",{"type":53,"tag":58,"props":385,"children":386},{},[387,392,394,399],{"type":53,"tag":62,"props":388,"children":389},{},[390],{"type":66,"value":391},"Note:",{"type":66,"value":393}," The \"Run audit checks\" phase creates ",{"type":53,"tag":62,"props":395,"children":396},{},[397],{"type":66,"value":398},"additional per-table tasks",{"type":66,"value":400}," dynamically in Step 4.2. These per-table tasks track the systematic A–K checklist for each table independently.",{"type":53,"tag":402,"props":403,"children":404},"hr",{},[],{"type":53,"tag":91,"props":406,"children":408},{"id":407},"step-1-verify-site-deployment",[409],{"type":66,"value":410},"Step 1: Verify Site Deployment",{"type":53,"tag":58,"props":412,"children":413},{},[414,416,422],{"type":66,"value":415},"Use ",{"type":53,"tag":71,"props":417,"children":419},{"className":418},[],[420],{"type":66,"value":421},"Glob",{"type":66,"value":423}," to find:",{"type":53,"tag":425,"props":426,"children":427},"ul",{},[428,439],{"type":53,"tag":102,"props":429,"children":430},{},[431,437],{"type":53,"tag":71,"props":432,"children":434},{"className":433},[],[435],{"type":66,"value":436},"**\u002Fpowerpages.config.json",{"type":66,"value":438}," — identifies the project root",{"type":53,"tag":102,"props":440,"children":441},{},[442,448],{"type":53,"tag":71,"props":443,"children":445},{"className":444},[],[446],{"type":66,"value":447},"**\u002F.powerpages-site\u002Ftable-permissions\u002F*.tablepermission.yml",{"type":66,"value":449}," — existing permissions",{"type":53,"tag":58,"props":451,"children":452},{},[453,455,460,462,468],{"type":66,"value":454},"If no ",{"type":53,"tag":71,"props":456,"children":458},{"className":457},[],[459],{"type":66,"value":117},{"type":66,"value":461}," folder exists, stop and tell the user to deploy first using ",{"type":53,"tag":71,"props":463,"children":465},{"className":464},[],[466],{"type":66,"value":467},"\u002Fdeploy-site",{"type":66,"value":469},".\nIf 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.",{"type":53,"tag":402,"props":471,"children":472},{},[],{"type":53,"tag":91,"props":474,"children":476},{"id":475},"step-2-gather-configuration",[477],{"type":66,"value":478},"Step 2: Gather Configuration",{"type":53,"tag":480,"props":481,"children":483},"h3",{"id":482},"_21-read-web-roles",[484],{"type":66,"value":485},"2.1 Read Web Roles",{"type":53,"tag":58,"props":487,"children":488},{},[489,491,497,499,505,507,513,514,520,521,527],{"type":66,"value":490},"Read all files matching ",{"type":53,"tag":71,"props":492,"children":494},{"className":493},[],[495],{"type":66,"value":496},"**\u002F.powerpages-site\u002Fweb-roles\u002F*.yml",{"type":66,"value":498},". Extract ",{"type":53,"tag":71,"props":500,"children":502},{"className":501},[],[503],{"type":66,"value":504},"id",{"type":66,"value":506},", ",{"type":53,"tag":71,"props":508,"children":510},{"className":509},[],[511],{"type":66,"value":512},"name",{"type":66,"value":506},{"type":53,"tag":71,"props":515,"children":517},{"className":516},[],[518],{"type":66,"value":519},"anonymoususersrole",{"type":66,"value":506},{"type":53,"tag":71,"props":522,"children":524},{"className":523},[],[525],{"type":66,"value":526},"authenticatedusersrole",{"type":66,"value":528}," from each.",{"type":53,"tag":480,"props":530,"children":532},{"id":531},"_22-read-table-permissions",[533],{"type":66,"value":534},"2.2 Read Table Permissions",{"type":53,"tag":58,"props":536,"children":537},{},[538,539,544],{"type":66,"value":490},{"type":53,"tag":71,"props":540,"children":542},{"className":541},[],[543],{"type":66,"value":447},{"type":66,"value":545},". For each permission, extract:",{"type":53,"tag":425,"props":547,"children":548},{},[549,560,571,582,628,639,657],{"type":53,"tag":102,"props":550,"children":551},{},[552,558],{"type":53,"tag":71,"props":553,"children":555},{"className":554},[],[556],{"type":66,"value":557},"entityname",{"type":66,"value":559}," (permission name)",{"type":53,"tag":102,"props":561,"children":562},{},[563,569],{"type":53,"tag":71,"props":564,"children":566},{"className":565},[],[567],{"type":66,"value":568},"entitylogicalname",{"type":66,"value":570}," (table)",{"type":53,"tag":102,"props":572,"children":573},{},[574,580],{"type":53,"tag":71,"props":575,"children":577},{"className":576},[],[578],{"type":66,"value":579},"scope",{"type":66,"value":581}," (numeric code)",{"type":53,"tag":102,"props":583,"children":584},{},[585,591,592,598,599,605,606,612,613,619,620,626],{"type":53,"tag":71,"props":586,"children":588},{"className":587},[],[589],{"type":66,"value":590},"read",{"type":66,"value":506},{"type":53,"tag":71,"props":593,"children":595},{"className":594},[],[596],{"type":66,"value":597},"create",{"type":66,"value":506},{"type":53,"tag":71,"props":600,"children":602},{"className":601},[],[603],{"type":66,"value":604},"write",{"type":66,"value":506},{"type":53,"tag":71,"props":607,"children":609},{"className":608},[],[610],{"type":66,"value":611},"delete",{"type":66,"value":506},{"type":53,"tag":71,"props":614,"children":616},{"className":615},[],[617],{"type":66,"value":618},"append",{"type":66,"value":506},{"type":53,"tag":71,"props":621,"children":623},{"className":622},[],[624],{"type":66,"value":625},"appendto",{"type":66,"value":627}," (boolean flags)",{"type":53,"tag":102,"props":629,"children":630},{},[631,637],{"type":53,"tag":71,"props":632,"children":634},{"className":633},[],[635],{"type":66,"value":636},"adx_entitypermission_webrole",{"type":66,"value":638}," (array of web role UUIDs)",{"type":53,"tag":102,"props":640,"children":641},{},[642,648,649,655],{"type":53,"tag":71,"props":643,"children":645},{"className":644},[],[646],{"type":66,"value":647},"contactrelationship",{"type":66,"value":506},{"type":53,"tag":71,"props":650,"children":652},{"className":651},[],[653],{"type":66,"value":654},"accountrelationship",{"type":66,"value":656}," (if Contact\u002FAccount scope)",{"type":53,"tag":102,"props":658,"children":659},{},[660,666,667,673],{"type":53,"tag":71,"props":661,"children":663},{"className":662},[],[664],{"type":66,"value":665},"parententitypermission",{"type":66,"value":506},{"type":53,"tag":71,"props":668,"children":670},{"className":669},[],[671],{"type":66,"value":672},"parentrelationship",{"type":66,"value":674}," (if parent scope)",{"type":53,"tag":480,"props":676,"children":678},{"id":677},"_23-analyze-site-code",[679],{"type":66,"value":680},"2.3 Analyze Site Code",{"type":53,"tag":58,"props":682,"children":683},{},[684],{"type":66,"value":685},"Search the site source code for:",{"type":53,"tag":425,"props":687,"children":688},{},[689,702,714,747],{"type":53,"tag":102,"props":690,"children":691},{},[692,694,700],{"type":66,"value":693},"Web API calls (",{"type":53,"tag":71,"props":695,"children":697},{"className":696},[],[698],{"type":66,"value":699},"\u002F_api\u002F",{"type":66,"value":701},")",{"type":53,"tag":102,"props":703,"children":704},{},[705,707,713],{"type":66,"value":706},"Lookup bindings (",{"type":53,"tag":71,"props":708,"children":710},{"className":709},[],[711],{"type":66,"value":712},"@odata.bind",{"type":66,"value":701},{"type":53,"tag":102,"props":715,"children":716},{},[717,719,725,726,732,733,739,740,746],{"type":66,"value":718},"File uploads (",{"type":53,"tag":71,"props":720,"children":722},{"className":721},[],[723],{"type":66,"value":724},"uploadFileColumn",{"type":66,"value":506},{"type":53,"tag":71,"props":727,"children":729},{"className":728},[],[730],{"type":66,"value":731},"uploadFile",{"type":66,"value":506},{"type":53,"tag":71,"props":734,"children":736},{"className":735},[],[737],{"type":66,"value":738},"upload*Photo",{"type":66,"value":506},{"type":53,"tag":71,"props":741,"children":743},{"className":742},[],[744],{"type":66,"value":745},"upload*Image",{"type":66,"value":701},{"type":53,"tag":102,"props":748,"children":749},{},[750,756,758,763,764,770,771,777],{"type":53,"tag":71,"props":751,"children":753},{"className":752},[],[754],{"type":66,"value":755},"$expand",{"type":66,"value":757}," usage (",{"type":53,"tag":71,"props":759,"children":761},{"className":760},[],[762],{"type":66,"value":755},{"type":66,"value":506},{"type":53,"tag":71,"props":765,"children":767},{"className":766},[],[768],{"type":66,"value":769},"buildExpandClause",{"type":66,"value":506},{"type":53,"tag":71,"props":772,"children":774},{"className":773},[],[775],{"type":66,"value":776},"ExpandOption",{"type":66,"value":701},{"type":53,"tag":58,"props":779,"children":780},{},[781,783,789],{"type":66,"value":782},"Also check for ",{"type":53,"tag":71,"props":784,"children":786},{"className":785},[],[787],{"type":66,"value":788},".datamodel-manifest.json",{"type":66,"value":790}," in the project root for the authoritative table list.",{"type":53,"tag":58,"props":792,"children":793},{},[794,796,801],{"type":66,"value":795},"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 ",{"type":53,"tag":71,"props":797,"children":799},{"className":798},[],[800],{"type":66,"value":755},{"type":66,"value":802}," (these need read permissions too).",{"type":53,"tag":480,"props":804,"children":806},{"id":805},"_24-run-shared-schema-validator",[807],{"type":66,"value":808},"2.4 Run Shared Schema Validator",{"type":53,"tag":58,"props":810,"children":811},{},[812],{"type":66,"value":813},"Run the shared validator against the existing site:",{"type":53,"tag":815,"props":816,"children":821},"pre",{"className":817,"code":818,"language":819,"meta":820,"style":820},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","node \"${PLUGIN_ROOT}\u002Fscripts\u002Fvalidate-permissions-schema.js\" --projectRoot \"\u003CPROJECT_ROOT>\"\n","bash","",[822],{"type":53,"tag":71,"props":823,"children":824},{"__ignoreMap":820},[825],{"type":53,"tag":826,"props":827,"children":830},"span",{"class":828,"line":829},"line",1,[831,837,843,849,854,860,865,870,875,880],{"type":53,"tag":826,"props":832,"children":834},{"style":833},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[835],{"type":66,"value":836},"node",{"type":53,"tag":826,"props":838,"children":840},{"style":839},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[841],{"type":66,"value":842}," \"${",{"type":53,"tag":826,"props":844,"children":846},{"style":845},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[847],{"type":66,"value":848},"PLUGIN_ROOT",{"type":53,"tag":826,"props":850,"children":851},{"style":839},[852],{"type":66,"value":853},"}",{"type":53,"tag":826,"props":855,"children":857},{"style":856},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[858],{"type":66,"value":859},"\u002Fscripts\u002Fvalidate-permissions-schema.js",{"type":53,"tag":826,"props":861,"children":862},{"style":839},[863],{"type":66,"value":864},"\"",{"type":53,"tag":826,"props":866,"children":867},{"style":856},[868],{"type":66,"value":869}," --projectRoot",{"type":53,"tag":826,"props":871,"children":872},{"style":839},[873],{"type":66,"value":874}," \"",{"type":53,"tag":826,"props":876,"children":877},{"style":856},[878],{"type":66,"value":879},"\u003CPROJECT_ROOT>",{"type":53,"tag":826,"props":881,"children":882},{"style":839},[883],{"type":66,"value":884},"\"\n",{"type":53,"tag":58,"props":886,"children":887},{},[888],{"type":66,"value":889},"Parse the JSON output and carry the findings into the audit. Treat:",{"type":53,"tag":425,"props":891,"children":892},{},[893,909,923],{"type":53,"tag":102,"props":894,"children":895},{},[896,902,904],{"type":53,"tag":71,"props":897,"children":899},{"className":898},[],[900],{"type":66,"value":901},"error",{"type":66,"value":903}," findings as ",{"type":53,"tag":62,"props":905,"children":906},{},[907],{"type":66,"value":908},"critical",{"type":53,"tag":102,"props":910,"children":911},{},[912,918,919],{"type":53,"tag":71,"props":913,"children":915},{"className":914},[],[916],{"type":66,"value":917},"warning",{"type":66,"value":903},{"type":53,"tag":62,"props":920,"children":921},{},[922],{"type":66,"value":917},{"type":53,"tag":102,"props":924,"children":925},{},[926,932,933],{"type":53,"tag":71,"props":927,"children":929},{"className":928},[],[930],{"type":66,"value":931},"info",{"type":66,"value":903},{"type":53,"tag":62,"props":934,"children":935},{},[936],{"type":66,"value":931},{"type":53,"tag":58,"props":938,"children":939},{},[940],{"type":66,"value":941},"These findings should be included in the final audit report even if the later code\u002FDataverse analysis also finds additional issues.",{"type":53,"tag":58,"props":943,"children":944},{},[945],{"type":66,"value":946},"After Step 3.1 determines the environment URL, if this audit is running locally with Dataverse access available, rerun the shared validator with live relationship verification enabled and merge any additional findings:",{"type":53,"tag":815,"props":948,"children":950},{"className":817,"code":949,"language":819,"meta":820,"style":820},"node \"${PLUGIN_ROOT}\u002Fscripts\u002Fvalidate-permissions-schema.js\" --projectRoot \"\u003CPROJECT_ROOT>\" --validate-dataverse-relationships --envUrl \"\u003CenvUrl>\"\n",[951],{"type":53,"tag":71,"props":952,"children":953},{"__ignoreMap":820},[954],{"type":53,"tag":826,"props":955,"children":956},{"class":828,"line":829},[957,961,965,969,973,977,981,985,989,993,997,1002,1007,1011,1016],{"type":53,"tag":826,"props":958,"children":959},{"style":833},[960],{"type":66,"value":836},{"type":53,"tag":826,"props":962,"children":963},{"style":839},[964],{"type":66,"value":842},{"type":53,"tag":826,"props":966,"children":967},{"style":845},[968],{"type":66,"value":848},{"type":53,"tag":826,"props":970,"children":971},{"style":839},[972],{"type":66,"value":853},{"type":53,"tag":826,"props":974,"children":975},{"style":856},[976],{"type":66,"value":859},{"type":53,"tag":826,"props":978,"children":979},{"style":839},[980],{"type":66,"value":864},{"type":53,"tag":826,"props":982,"children":983},{"style":856},[984],{"type":66,"value":869},{"type":53,"tag":826,"props":986,"children":987},{"style":839},[988],{"type":66,"value":874},{"type":53,"tag":826,"props":990,"children":991},{"style":856},[992],{"type":66,"value":879},{"type":53,"tag":826,"props":994,"children":995},{"style":839},[996],{"type":66,"value":864},{"type":53,"tag":826,"props":998,"children":999},{"style":856},[1000],{"type":66,"value":1001}," --validate-dataverse-relationships",{"type":53,"tag":826,"props":1003,"children":1004},{"style":856},[1005],{"type":66,"value":1006}," --envUrl",{"type":53,"tag":826,"props":1008,"children":1009},{"style":839},[1010],{"type":66,"value":874},{"type":53,"tag":826,"props":1012,"children":1013},{"style":856},[1014],{"type":66,"value":1015},"\u003CenvUrl>",{"type":53,"tag":826,"props":1017,"children":1018},{"style":839},[1019],{"type":66,"value":884},{"type":53,"tag":58,"props":1021,"children":1022},{},[1023,1025,1030],{"type":66,"value":1024},"Use this Dataverse-backed relationship validation only for local runs. Do ",{"type":53,"tag":62,"props":1026,"children":1027},{},[1028],{"type":66,"value":1029},"not",{"type":66,"value":1031}," require it in CI or other offline contexts.",{"type":53,"tag":402,"props":1033,"children":1034},{},[],{"type":53,"tag":91,"props":1036,"children":1038},{"id":1037},"step-3-analyze-discover-dataverse-api",[1039],{"type":66,"value":1040},"Step 3: Analyze & Discover (Dataverse API)",{"type":53,"tag":58,"props":1042,"children":1043},{},[1044],{"type":66,"value":1045},"Use deterministic Node.js scripts for all Dataverse API calls. These scripts handle auth token acquisition, HTTP requests, and JSON parsing consistently.",{"type":53,"tag":480,"props":1047,"children":1049},{"id":1048},"_31-get-environment-url",[1050],{"type":66,"value":1051},"3.1 Get Environment URL",{"type":53,"tag":815,"props":1053,"children":1055},{"className":817,"code":1054,"language":819,"meta":820,"style":820},"pac env who\n",[1056],{"type":53,"tag":71,"props":1057,"children":1058},{"__ignoreMap":820},[1059],{"type":53,"tag":826,"props":1060,"children":1061},{"class":828,"line":829},[1062,1067,1072],{"type":53,"tag":826,"props":1063,"children":1064},{"style":833},[1065],{"type":66,"value":1066},"pac",{"type":53,"tag":826,"props":1068,"children":1069},{"style":856},[1070],{"type":66,"value":1071}," env",{"type":53,"tag":826,"props":1073,"children":1074},{"style":856},[1075],{"type":66,"value":1076}," who\n",{"type":53,"tag":58,"props":1078,"children":1079},{},[1080,1082,1088,1090,1096,1098,1103],{"type":66,"value":1081},"Extract the ",{"type":53,"tag":71,"props":1083,"children":1085},{"className":1084},[],[1086],{"type":66,"value":1087},"Environment URL",{"type":66,"value":1089}," (e.g., ",{"type":53,"tag":71,"props":1091,"children":1093},{"className":1092},[],[1094],{"type":66,"value":1095},"https:\u002F\u002Forg12345.crm.dynamics.com",{"type":66,"value":1097},") and use it as ",{"type":53,"tag":71,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":66,"value":1015},{"type":66,"value":1104}," in subsequent script calls.",{"type":53,"tag":480,"props":1106,"children":1108},{"id":1107},"_32-query-lookup-columns",[1109],{"type":66,"value":1110},"3.2 Query Lookup Columns",{"type":53,"tag":58,"props":1112,"children":1113},{},[1114,1116,1121,1123,1128],{"type":66,"value":1115},"For each table that has permissions with ",{"type":53,"tag":71,"props":1117,"children":1119},{"className":1118},[],[1120],{"type":66,"value":597},{"type":66,"value":1122}," or ",{"type":53,"tag":71,"props":1124,"children":1126},{"className":1125},[],[1127],{"type":66,"value":604},{"type":66,"value":1129}," enabled, use the lookup query script:",{"type":53,"tag":815,"props":1131,"children":1133},{"className":817,"code":1132,"language":819,"meta":820,"style":820},"node \"${PLUGIN_ROOT}\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-lookups.js\" --envUrl \"\u003CenvUrl>\" --table \"\u003Ctable_logical_name>\"\n",[1134],{"type":53,"tag":71,"props":1135,"children":1136},{"__ignoreMap":820},[1137],{"type":53,"tag":826,"props":1138,"children":1139},{"class":828,"line":829},[1140,1144,1148,1152,1156,1161,1165,1169,1173,1177,1181,1186,1190,1195],{"type":53,"tag":826,"props":1141,"children":1142},{"style":833},[1143],{"type":66,"value":836},{"type":53,"tag":826,"props":1145,"children":1146},{"style":839},[1147],{"type":66,"value":842},{"type":53,"tag":826,"props":1149,"children":1150},{"style":845},[1151],{"type":66,"value":848},{"type":53,"tag":826,"props":1153,"children":1154},{"style":839},[1155],{"type":66,"value":853},{"type":53,"tag":826,"props":1157,"children":1158},{"style":856},[1159],{"type":66,"value":1160},"\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-lookups.js",{"type":53,"tag":826,"props":1162,"children":1163},{"style":839},[1164],{"type":66,"value":864},{"type":53,"tag":826,"props":1166,"children":1167},{"style":856},[1168],{"type":66,"value":1006},{"type":53,"tag":826,"props":1170,"children":1171},{"style":839},[1172],{"type":66,"value":874},{"type":53,"tag":826,"props":1174,"children":1175},{"style":856},[1176],{"type":66,"value":1015},{"type":53,"tag":826,"props":1178,"children":1179},{"style":839},[1180],{"type":66,"value":864},{"type":53,"tag":826,"props":1182,"children":1183},{"style":856},[1184],{"type":66,"value":1185}," --table",{"type":53,"tag":826,"props":1187,"children":1188},{"style":839},[1189],{"type":66,"value":874},{"type":53,"tag":826,"props":1191,"children":1192},{"style":856},[1193],{"type":66,"value":1194},"\u003Ctable_logical_name>",{"type":53,"tag":826,"props":1196,"children":1197},{"style":839},[1198],{"type":66,"value":884},{"type":53,"tag":58,"props":1200,"children":1201},{},[1202,1204,1210],{"type":66,"value":1203},"The script returns a JSON array of ",{"type":53,"tag":71,"props":1205,"children":1207},{"className":1206},[],[1208],{"type":66,"value":1209},"{ logicalName, targets }",{"type":66,"value":1211}," for each lookup column. Capture this output for the maps described below.",{"type":53,"tag":58,"props":1213,"children":1214},{},[1215,1217,1222],{"type":66,"value":1216},"After querying ",{"type":53,"tag":62,"props":1218,"children":1219},{},[1220],{"type":66,"value":1221},"all",{"type":66,"value":1223}," tables with create or write permissions, build two maps from the combined results:",{"type":53,"tag":98,"props":1225,"children":1226},{},[1227,1244],{"type":53,"tag":102,"props":1228,"children":1229},{},[1230,1235,1237,1242],{"type":53,"tag":62,"props":1231,"children":1232},{},[1233],{"type":66,"value":1234},"Source map",{"type":66,"value":1236}," (table → lookup columns): For each queried table, record which lookup columns it has and their targets. Used in Section H2 to check ",{"type":53,"tag":71,"props":1238,"children":1240},{"className":1239},[],[1241],{"type":66,"value":625},{"type":66,"value":1243}," on the source table.",{"type":53,"tag":102,"props":1245,"children":1246},{},[1247,1252,1254,1260,1262,1267],{"type":53,"tag":62,"props":1248,"children":1249},{},[1250],{"type":66,"value":1251},"Reverse target map",{"type":66,"value":1253}," (target table → list of source tables): For each target table found in any lookup's ",{"type":53,"tag":71,"props":1255,"children":1257},{"className":1256},[],[1258],{"type":66,"value":1259},"targets",{"type":66,"value":1261}," array, record which source table(s) reference it. Used in Section H to check ",{"type":53,"tag":71,"props":1263,"children":1265},{"className":1264},[],[1266],{"type":66,"value":618},{"type":66,"value":1268}," on the target table.",{"type":53,"tag":58,"props":1270,"children":1271},{},[1272,1274,1280,1282],{"type":66,"value":1273},"Example: querying ",{"type":53,"tag":71,"props":1275,"children":1277},{"className":1276},[],[1278],{"type":66,"value":1279},"order_item",{"type":66,"value":1281}," returns ",{"type":53,"tag":71,"props":1283,"children":1285},{"className":1284},[],[1286],{"type":66,"value":1287},"[{ logicalName: \"cr4fc_orderid\", targets: [\"cr4fc_order\"] }]",{"type":53,"tag":425,"props":1289,"children":1290},{},[1291,1302],{"type":53,"tag":102,"props":1292,"children":1293},{},[1294,1296],{"type":66,"value":1295},"Source map: ",{"type":53,"tag":71,"props":1297,"children":1299},{"className":1298},[],[1300],{"type":66,"value":1301},"order_item → [{ column: \"cr4fc_orderid\", targets: [\"cr4fc_order\"] }]",{"type":53,"tag":102,"props":1303,"children":1304},{},[1305,1307],{"type":66,"value":1306},"Reverse target map: ",{"type":53,"tag":71,"props":1308,"children":1310},{"className":1309},[],[1311],{"type":66,"value":1312},"cr4fc_order → [{ sourceTable: \"order_item\", column: \"cr4fc_orderid\" }]",{"type":53,"tag":58,"props":1314,"children":1315},{},[1316],{"type":66,"value":1317},"Both maps are used in Sections H and H2:",{"type":53,"tag":425,"props":1319,"children":1320},{},[1321,1341],{"type":53,"tag":102,"props":1322,"children":1323},{},[1324,1326,1331,1333,1339],{"type":66,"value":1325},"The ",{"type":53,"tag":62,"props":1327,"children":1328},{},[1329],{"type":66,"value":1330},"source table",{"type":66,"value":1332}," (with the lookup) needs ",{"type":53,"tag":71,"props":1334,"children":1336},{"className":1335},[],[1337],{"type":66,"value":1338},"appendto: true",{"type":66,"value":1340}," — it links TO other records (checked via the source map)",{"type":53,"tag":102,"props":1342,"children":1343},{},[1344,1346,1351,1353,1358,1360,1366],{"type":66,"value":1345},"Each ",{"type":53,"tag":62,"props":1347,"children":1348},{},[1349],{"type":66,"value":1350},"target table",{"type":66,"value":1352}," in ",{"type":53,"tag":71,"props":1354,"children":1356},{"className":1355},[],[1357],{"type":66,"value":1259},{"type":66,"value":1359}," needs ",{"type":53,"tag":71,"props":1361,"children":1363},{"className":1362},[],[1364],{"type":66,"value":1365},"append: true",{"type":66,"value":1367}," — other records link TO it (checked via the reverse target map)",{"type":53,"tag":480,"props":1369,"children":1371},{"id":1370},"_33-query-relationships",[1372],{"type":66,"value":1373},"3.3 Query Relationships",{"type":53,"tag":58,"props":1375,"children":1376},{},[1377],{"type":66,"value":1378},"For tables with parent-scope permissions, verify the relationship names using the relationship query script:",{"type":53,"tag":815,"props":1380,"children":1382},{"className":817,"code":1381,"language":819,"meta":820,"style":820},"node \"${PLUGIN_ROOT}\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-relationships.js\" --envUrl \"\u003CenvUrl>\" --table \"\u003Cparent_table>\"\n",[1383],{"type":53,"tag":71,"props":1384,"children":1385},{"__ignoreMap":820},[1386],{"type":53,"tag":826,"props":1387,"children":1388},{"class":828,"line":829},[1389,1393,1397,1401,1405,1410,1414,1418,1422,1426,1430,1434,1438,1443],{"type":53,"tag":826,"props":1390,"children":1391},{"style":833},[1392],{"type":66,"value":836},{"type":53,"tag":826,"props":1394,"children":1395},{"style":839},[1396],{"type":66,"value":842},{"type":53,"tag":826,"props":1398,"children":1399},{"style":845},[1400],{"type":66,"value":848},{"type":53,"tag":826,"props":1402,"children":1403},{"style":839},[1404],{"type":66,"value":853},{"type":53,"tag":826,"props":1406,"children":1407},{"style":856},[1408],{"type":66,"value":1409},"\u002Fskills\u002Faudit-permissions\u002Fscripts\u002Fquery-table-relationships.js",{"type":53,"tag":826,"props":1411,"children":1412},{"style":839},[1413],{"type":66,"value":864},{"type":53,"tag":826,"props":1415,"children":1416},{"style":856},[1417],{"type":66,"value":1006},{"type":53,"tag":826,"props":1419,"children":1420},{"style":839},[1421],{"type":66,"value":874},{"type":53,"tag":826,"props":1423,"children":1424},{"style":856},[1425],{"type":66,"value":1015},{"type":53,"tag":826,"props":1427,"children":1428},{"style":839},[1429],{"type":66,"value":864},{"type":53,"tag":826,"props":1431,"children":1432},{"style":856},[1433],{"type":66,"value":1185},{"type":53,"tag":826,"props":1435,"children":1436},{"style":839},[1437],{"type":66,"value":874},{"type":53,"tag":826,"props":1439,"children":1440},{"style":856},[1441],{"type":66,"value":1442},"\u003Cparent_table>",{"type":53,"tag":826,"props":1444,"children":1445},{"style":839},[1446],{"type":66,"value":884},{"type":53,"tag":58,"props":1448,"children":1449},{},[1450,1451,1457,1459,1465,1467,1472],{"type":66,"value":1203},{"type":53,"tag":71,"props":1452,"children":1454},{"className":1453},[],[1455],{"type":66,"value":1456},"{ schemaName, referencedEntity, referencingEntity, referencingAttribute }",{"type":66,"value":1458},". Use ",{"type":53,"tag":71,"props":1460,"children":1462},{"className":1461},[],[1463],{"type":66,"value":1464},"schemaName",{"type":66,"value":1466}," to validate the ",{"type":53,"tag":71,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":66,"value":672},{"type":66,"value":1473}," value in parent-scope permissions.",{"type":53,"tag":480,"props":1475,"children":1477},{"id":1476},"error-handling",[1478],{"type":66,"value":1479},"Error Handling",{"type":53,"tag":58,"props":1481,"children":1482},{},[1483],{"type":66,"value":1484},"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.",{"type":53,"tag":402,"props":1486,"children":1487},{},[],{"type":53,"tag":91,"props":1489,"children":1491},{"id":1490},"step-4-run-audit-checks",[1492],{"type":66,"value":1493},"Step 4: Run Audit Checks",{"type":53,"tag":58,"props":1495,"children":1496},{},[1497,1499,1504,1505,1510,1511,1516,1518,1524],{"type":66,"value":1498},"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 ",{"type":53,"tag":71,"props":1500,"children":1502},{"className":1501},[],[1503],{"type":66,"value":908},{"type":66,"value":506},{"type":53,"tag":71,"props":1506,"children":1508},{"className":1507},[],[1509],{"type":66,"value":917},{"type":66,"value":506},{"type":53,"tag":71,"props":1512,"children":1514},{"className":1513},[],[1515],{"type":66,"value":931},{"type":66,"value":1517},", or ",{"type":53,"tag":71,"props":1519,"children":1521},{"className":1520},[],[1522],{"type":66,"value":1523},"pass",{"type":66,"value":1525},".",{"type":53,"tag":480,"props":1527,"children":1529},{"id":1528},"_41-build-audit-inventory",[1530],{"type":66,"value":1531},"4.1 Build Audit Inventory",{"type":53,"tag":58,"props":1533,"children":1534},{},[1535],{"type":66,"value":1536},"First, build a combined list of all tables to audit from two sources:",{"type":53,"tag":98,"props":1538,"children":1539},{},[1540,1550],{"type":53,"tag":102,"props":1541,"children":1542},{},[1543,1548],{"type":53,"tag":62,"props":1544,"children":1545},{},[1546],{"type":66,"value":1547},"Tables referenced in code",{"type":66,"value":1549}," (from Step 2.3) — these may or may not have permissions",{"type":53,"tag":102,"props":1551,"children":1552},{},[1553,1558],{"type":53,"tag":62,"props":1554,"children":1555},{},[1556],{"type":66,"value":1557},"Tables with existing permissions",{"type":66,"value":1559}," (from Step 2.2) — these may or may not be referenced in code",{"type":53,"tag":58,"props":1561,"children":1562},{},[1563],{"type":66,"value":1564},"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?\"",{"type":53,"tag":480,"props":1566,"children":1568},{"id":1567},"_42-create-per-table-audit-tasks",[1569],{"type":66,"value":1570},"4.2 Create Per-Table Audit Tasks",{"type":53,"tag":58,"props":1572,"children":1573},{},[1574],{"type":66,"value":1575},"For each table in the audit inventory, create a task:",{"type":53,"tag":815,"props":1577,"children":1581},{"className":1578,"code":1580,"language":66},[1579],"language-text","TaskCreate:\n  subject: \"Audit \u003Ctable_logical_name>\"\n  activeForm: \"Auditing \u003Ctable_display_name> permissions\"\n  description: \"Run all audit checks for \u003Ctable_logical_name>\"\n",[1582],{"type":53,"tag":71,"props":1583,"children":1584},{"__ignoreMap":820},[1585],{"type":66,"value":1580},{"type":53,"tag":58,"props":1587,"children":1588},{},[1589],{"type":66,"value":1590},"Also create a summary task:",{"type":53,"tag":815,"props":1592,"children":1595},{"className":1593,"code":1594,"language":66},[1579],"TaskCreate:\n  subject: \"Compile audit findings\"\n  activeForm: \"Compiling audit findings\"\n  description: \"Combine all per-table findings into the final report\"\n",[1596],{"type":53,"tag":71,"props":1597,"children":1598},{"__ignoreMap":820},[1599],{"type":66,"value":1594},{"type":53,"tag":58,"props":1601,"children":1602},{},[1603,1604,1610],{"type":66,"value":415},{"type":53,"tag":71,"props":1605,"children":1607},{"className":1606},[],[1608],{"type":66,"value":1609},"TaskList",{"type":66,"value":1611}," at any point to review progress and see which tables still need auditing.",{"type":53,"tag":480,"props":1613,"children":1615},{"id":1614},"_43-per-table-audit-checklist",[1616],{"type":66,"value":1617},"4.3 Per-Table Audit Checklist",{"type":53,"tag":58,"props":1619,"children":1620},{},[1621,1623,1628,1630,1635,1637,1642],{"type":66,"value":1622},"For each table, mark its task ",{"type":53,"tag":71,"props":1624,"children":1626},{"className":1625},[],[1627],{"type":66,"value":214},{"type":66,"value":1629}," and run through the following checks ",{"type":53,"tag":62,"props":1631,"children":1632},{},[1633],{"type":66,"value":1634},"in order",{"type":66,"value":1636},". For every finding, note the ",{"type":53,"tag":62,"props":1638,"children":1639},{},[1640],{"type":66,"value":1641},"specific evidence",{"type":66,"value":1643}," (file path, permission name, code pattern) that supports it. Skip checks that don't apply to this table.",{"type":53,"tag":58,"props":1645,"children":1646},{},[1647],{"type":53,"tag":62,"props":1648,"children":1649},{},[1650],{"type":66,"value":1651},"A. Permission Existence",{"type":53,"tag":58,"props":1653,"children":1654},{},[1655],{"type":66,"value":1656},"Does this table have a table permission?",{"type":53,"tag":425,"props":1658,"children":1659},{},[1660,1725,1784],{"type":53,"tag":102,"props":1661,"children":1662},{},[1663,1665,1670,1672],{"type":66,"value":1664},"If the table is referenced in code but has ",{"type":53,"tag":62,"props":1666,"children":1667},{},[1668],{"type":66,"value":1669},"no permission",{"type":66,"value":1671}," → finding:\n",{"type":53,"tag":425,"props":1673,"children":1674},{},[1675,1690,1705,1715],{"type":53,"tag":102,"props":1676,"children":1677},{},[1678,1683,1685],{"type":53,"tag":62,"props":1679,"children":1680},{},[1681],{"type":66,"value":1682},"Severity:",{"type":66,"value":1684}," ",{"type":53,"tag":71,"props":1686,"children":1688},{"className":1687},[],[1689],{"type":66,"value":908},{"type":53,"tag":102,"props":1691,"children":1692},{},[1693,1698,1699],{"type":53,"tag":62,"props":1694,"children":1695},{},[1696],{"type":66,"value":1697},"Title:",{"type":66,"value":1684},{"type":53,"tag":71,"props":1700,"children":1702},{"className":1701},[],[1703],{"type":66,"value":1704},"Missing permission for \u003Ctable>",{"type":53,"tag":102,"props":1706,"children":1707},{},[1708,1713],{"type":53,"tag":62,"props":1709,"children":1710},{},[1711],{"type":66,"value":1712},"Reasoning:",{"type":66,"value":1714}," Which code files reference this table and what operations they perform",{"type":53,"tag":102,"props":1716,"children":1717},{},[1718,1723],{"type":53,"tag":62,"props":1719,"children":1720},{},[1721],{"type":66,"value":1722},"Fix:",{"type":66,"value":1724}," Create a permission with the appropriate scope and CRUD flags",{"type":53,"tag":102,"props":1726,"children":1727},{},[1728,1730,1735,1736],{"type":66,"value":1729},"If a permission exists but the table is ",{"type":53,"tag":62,"props":1731,"children":1732},{},[1733],{"type":66,"value":1734},"not referenced in code",{"type":66,"value":1671},{"type":53,"tag":425,"props":1737,"children":1738},{},[1739,1752,1766,1775],{"type":53,"tag":102,"props":1740,"children":1741},{},[1742,1746,1747],{"type":53,"tag":62,"props":1743,"children":1744},{},[1745],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":1748,"children":1750},{"className":1749},[],[1751],{"type":66,"value":931},{"type":53,"tag":102,"props":1753,"children":1754},{},[1755,1759,1760],{"type":53,"tag":62,"props":1756,"children":1757},{},[1758],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":1761,"children":1763},{"className":1762},[],[1764],{"type":66,"value":1765},"Unused permission for \u003Ctable>",{"type":53,"tag":102,"props":1767,"children":1768},{},[1769,1773],{"type":53,"tag":62,"props":1770,"children":1771},{},[1772],{"type":66,"value":1712},{"type":66,"value":1774}," The table is not referenced in any source code — the permission may be unnecessary",{"type":53,"tag":102,"props":1776,"children":1777},{},[1778,1782],{"type":53,"tag":62,"props":1779,"children":1780},{},[1781],{"type":66,"value":1722},{"type":66,"value":1783}," Review whether this permission is still needed",{"type":53,"tag":102,"props":1785,"children":1786},{},[1787,1789,1794],{"type":66,"value":1788},"If both exist → ",{"type":53,"tag":71,"props":1790,"children":1792},{"className":1791},[],[1793],{"type":66,"value":1523},{"type":66,"value":1795},", proceed to remaining checks",{"type":53,"tag":58,"props":1797,"children":1798},{},[1799],{"type":53,"tag":62,"props":1800,"children":1801},{},[1802],{"type":66,"value":1803},"B. Web Role Association",{"type":53,"tag":58,"props":1805,"children":1806},{},[1807],{"type":66,"value":1808},"Does the permission have web role(s) assigned?",{"type":53,"tag":425,"props":1810,"children":1811},{},[1812,1872],{"type":53,"tag":102,"props":1813,"children":1814},{},[1815,1817,1822,1824],{"type":66,"value":1816},"Check ",{"type":53,"tag":71,"props":1818,"children":1820},{"className":1819},[],[1821],{"type":66,"value":636},{"type":66,"value":1823}," — if empty or missing → finding:\n",{"type":53,"tag":425,"props":1825,"children":1826},{},[1827,1840,1854,1863],{"type":53,"tag":102,"props":1828,"children":1829},{},[1830,1834,1835],{"type":53,"tag":62,"props":1831,"children":1832},{},[1833],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":1836,"children":1838},{"className":1837},[],[1839],{"type":66,"value":917},{"type":53,"tag":102,"props":1841,"children":1842},{},[1843,1847,1848],{"type":53,"tag":62,"props":1844,"children":1845},{},[1846],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":1849,"children":1851},{"className":1850},[],[1852],{"type":66,"value":1853},"Permission \u003Cname> has no web role association",{"type":53,"tag":102,"props":1855,"children":1856},{},[1857,1861],{"type":53,"tag":62,"props":1858,"children":1859},{},[1860],{"type":66,"value":1712},{"type":66,"value":1862}," A permission without a web role has no effect — no users will receive this access",{"type":53,"tag":102,"props":1864,"children":1865},{},[1866,1870],{"type":53,"tag":62,"props":1867,"children":1868},{},[1869],{"type":66,"value":1722},{"type":66,"value":1871}," Associate with the appropriate web role",{"type":53,"tag":102,"props":1873,"children":1874},{},[1875,1877],{"type":66,"value":1876},"If roles are assigned → ",{"type":53,"tag":71,"props":1878,"children":1880},{"className":1879},[],[1881],{"type":66,"value":1523},{"type":53,"tag":58,"props":1883,"children":1884},{},[1885],{"type":53,"tag":62,"props":1886,"children":1887},{},[1888],{"type":66,"value":1889},"C. Scope Appropriateness",{"type":53,"tag":58,"props":1891,"children":1892},{},[1893],{"type":66,"value":1894},"Is the scope the least-privileged option that fits?",{"type":53,"tag":425,"props":1896,"children":1897},{},[1898,1939,2013,2032,2085],{"type":53,"tag":102,"props":1899,"children":1900},{},[1901,1903,1909,1910,1916,1917,1923,1925,1931,1932,1938],{"type":66,"value":1902},"Search the service code for scope-relevant patterns: contact-scoped filters (",{"type":53,"tag":71,"props":1904,"children":1906},{"className":1905},[],[1907],{"type":66,"value":1908},"getCurrentContactId",{"type":66,"value":506},{"type":53,"tag":71,"props":1911,"children":1913},{"className":1912},[],[1914],{"type":66,"value":1915},"_contactid_value",{"type":66,"value":506},{"type":53,"tag":71,"props":1918,"children":1920},{"className":1919},[],[1921],{"type":66,"value":1922},"contactid",{"type":66,"value":1924},") and account-scoped filters (",{"type":53,"tag":71,"props":1926,"children":1928},{"className":1927},[],[1929],{"type":66,"value":1930},"_accountid_value",{"type":66,"value":506},{"type":53,"tag":71,"props":1933,"children":1935},{"className":1934},[],[1936],{"type":66,"value":1937},"parentcustomerid",{"type":66,"value":701},{"type":53,"tag":102,"props":1940,"children":1941},{},[1942,1944,1950,1952,1957,1958,1963,1965],{"type":66,"value":1943},"If Global scope (",{"type":53,"tag":71,"props":1945,"children":1947},{"className":1946},[],[1948],{"type":66,"value":1949},"756150000",{"type":66,"value":1951},") with ",{"type":53,"tag":71,"props":1953,"children":1955},{"className":1954},[],[1956],{"type":66,"value":604},{"type":66,"value":1122},{"type":53,"tag":71,"props":1959,"children":1961},{"className":1960},[],[1962],{"type":66,"value":611},{"type":66,"value":1964}," enabled → finding:\n",{"type":53,"tag":425,"props":1966,"children":1967},{},[1968,1981,1995,2004],{"type":53,"tag":102,"props":1969,"children":1970},{},[1971,1975,1976],{"type":53,"tag":62,"props":1972,"children":1973},{},[1974],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":1977,"children":1979},{"className":1978},[],[1980],{"type":66,"value":917},{"type":53,"tag":102,"props":1982,"children":1983},{},[1984,1988,1989],{"type":53,"tag":62,"props":1985,"children":1986},{},[1987],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":1990,"children":1992},{"className":1991},[],[1993],{"type":66,"value":1994},"Global scope with write\u002Fdelete on \u003Ctable>",{"type":53,"tag":102,"props":1996,"children":1997},{},[1998,2002],{"type":53,"tag":62,"props":1999,"children":2000},{},[2001],{"type":66,"value":1712},{"type":66,"value":2003}," Any user with this role can modify\u002Fdelete any record in this table",{"type":53,"tag":102,"props":2005,"children":2006},{},[2007,2011],{"type":53,"tag":62,"props":2008,"children":2009},{},[2010],{"type":66,"value":1722},{"type":66,"value":2012}," Narrow to Contact or Account scope, or remove write\u002Fdelete if not needed",{"type":53,"tag":102,"props":2014,"children":2015},{},[2016,2018,2023,2025,2030],{"type":66,"value":2017},"If Global scope with only ",{"type":53,"tag":71,"props":2019,"children":2021},{"className":2020},[],[2022],{"type":66,"value":590},{"type":66,"value":2024}," → ",{"type":53,"tag":71,"props":2026,"children":2028},{"className":2027},[],[2029],{"type":66,"value":1523},{"type":66,"value":2031}," (acceptable for public reference data)",{"type":53,"tag":102,"props":2033,"children":2034},{},[2035,2037],{"type":66,"value":2036},"If code uses contact-scoped filters but permission uses Global → finding:\n",{"type":53,"tag":425,"props":2038,"children":2039},{},[2040,2053,2067,2076],{"type":53,"tag":102,"props":2041,"children":2042},{},[2043,2047,2048],{"type":53,"tag":62,"props":2044,"children":2045},{},[2046],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2049,"children":2051},{"className":2050},[],[2052],{"type":66,"value":917},{"type":53,"tag":102,"props":2054,"children":2055},{},[2056,2060,2061],{"type":53,"tag":62,"props":2057,"children":2058},{},[2059],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2062,"children":2064},{"className":2063},[],[2065],{"type":66,"value":2066},"Scope could be narrower for \u003Ctable>",{"type":53,"tag":102,"props":2068,"children":2069},{},[2070,2074],{"type":53,"tag":62,"props":2071,"children":2072},{},[2073],{"type":66,"value":1712},{"type":66,"value":2075}," Code filters by current contact but permission grants Global access",{"type":53,"tag":102,"props":2077,"children":2078},{},[2079,2083],{"type":53,"tag":62,"props":2080,"children":2081},{},[2082],{"type":66,"value":1722},{"type":66,"value":2084}," Narrow to Contact scope",{"type":53,"tag":102,"props":2086,"children":2087},{},[2088,2090],{"type":66,"value":2089},"Otherwise → ",{"type":53,"tag":71,"props":2091,"children":2093},{"className":2092},[],[2094],{"type":66,"value":1523},{"type":53,"tag":58,"props":2096,"children":2097},{},[2098],{"type":53,"tag":62,"props":2099,"children":2100},{},[2101],{"type":66,"value":2102},"D. Read Permission",{"type":53,"tag":58,"props":2104,"children":2105},{},[2106,2108,2113],{"type":66,"value":2107},"Is ",{"type":53,"tag":71,"props":2109,"children":2111},{"className":2110},[],[2112],{"type":66,"value":590},{"type":66,"value":2114}," correctly set?",{"type":53,"tag":425,"props":2116,"children":2117},{},[2118,2145,2211],{"type":53,"tag":102,"props":2119,"children":2120},{},[2121,2123,2129,2131,2137,2138,2144],{"type":66,"value":2122},"Search the service code for GET\u002Flist\u002Fget patterns for this table: API calls to ",{"type":53,"tag":71,"props":2124,"children":2126},{"className":2125},[],[2127],{"type":66,"value":2128},"\u002F_api\u002F\u003Centity_set>",{"type":66,"value":2130},", list\u002Fget functions (",{"type":53,"tag":71,"props":2132,"children":2134},{"className":2133},[],[2135],{"type":66,"value":2136},"list\u003CTableName>",{"type":66,"value":506},{"type":53,"tag":71,"props":2139,"children":2141},{"className":2140},[],[2142],{"type":66,"value":2143},"get\u003CTableName>",{"type":66,"value":701},{"type":53,"tag":102,"props":2146,"children":2147},{},[2148,2150,2156,2157],{"type":66,"value":2149},"If code reads this table but ",{"type":53,"tag":71,"props":2151,"children":2153},{"className":2152},[],[2154],{"type":66,"value":2155},"read: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":2158,"children":2159},{},[2160,2173,2187,2196],{"type":53,"tag":102,"props":2161,"children":2162},{},[2163,2167,2168],{"type":53,"tag":62,"props":2164,"children":2165},{},[2166],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2169,"children":2171},{"className":2170},[],[2172],{"type":66,"value":908},{"type":53,"tag":102,"props":2174,"children":2175},{},[2176,2180,2181],{"type":53,"tag":62,"props":2177,"children":2178},{},[2179],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2182,"children":2184},{"className":2183},[],[2185],{"type":66,"value":2186},"Missing read permission for \u003Ctable>",{"type":53,"tag":102,"props":2188,"children":2189},{},[2190,2194],{"type":53,"tag":62,"props":2191,"children":2192},{},[2193],{"type":66,"value":1712},{"type":66,"value":2195}," Code reads from this table but permission does not grant read access",{"type":53,"tag":102,"props":2197,"children":2198},{},[2199,2203,2205],{"type":53,"tag":62,"props":2200,"children":2201},{},[2202],{"type":66,"value":1722},{"type":66,"value":2204}," Enable ",{"type":53,"tag":71,"props":2206,"children":2208},{"className":2207},[],[2209],{"type":66,"value":2210},"read: true",{"type":53,"tag":102,"props":2212,"children":2213},{},[2214,2216,2221,2223],{"type":66,"value":2215},"If ",{"type":53,"tag":71,"props":2217,"children":2219},{"className":2218},[],[2220],{"type":66,"value":2210},{"type":66,"value":2222}," and code reads → ",{"type":53,"tag":71,"props":2224,"children":2226},{"className":2225},[],[2227],{"type":66,"value":1523},{"type":53,"tag":58,"props":2229,"children":2230},{},[2231],{"type":53,"tag":62,"props":2232,"children":2233},{},[2234],{"type":66,"value":2235},"E. Create Permission",{"type":53,"tag":58,"props":2237,"children":2238},{},[2239,2240,2245],{"type":66,"value":2107},{"type":53,"tag":71,"props":2241,"children":2243},{"className":2242},[],[2244],{"type":66,"value":597},{"type":66,"value":2114},{"type":53,"tag":425,"props":2247,"children":2248},{},[2249,2269,2334,2400],{"type":53,"tag":102,"props":2250,"children":2251},{},[2252,2254,2260,2262,2268],{"type":66,"value":2253},"Search the service code for POST\u002Fcreate patterns: POST method usage (",{"type":53,"tag":71,"props":2255,"children":2257},{"className":2256},[],[2258],{"type":66,"value":2259},"method: 'POST'",{"type":66,"value":2261},"), create functions (",{"type":53,"tag":71,"props":2263,"children":2265},{"className":2264},[],[2266],{"type":66,"value":2267},"create\u003CTableName>",{"type":66,"value":701},{"type":53,"tag":102,"props":2270,"children":2271},{},[2272,2274,2280,2281],{"type":66,"value":2273},"If code creates records but ",{"type":53,"tag":71,"props":2275,"children":2277},{"className":2276},[],[2278],{"type":66,"value":2279},"create: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":2282,"children":2283},{},[2284,2297,2311,2320],{"type":53,"tag":102,"props":2285,"children":2286},{},[2287,2291,2292],{"type":53,"tag":62,"props":2288,"children":2289},{},[2290],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2293,"children":2295},{"className":2294},[],[2296],{"type":66,"value":908},{"type":53,"tag":102,"props":2298,"children":2299},{},[2300,2304,2305],{"type":53,"tag":62,"props":2301,"children":2302},{},[2303],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2306,"children":2308},{"className":2307},[],[2309],{"type":66,"value":2310},"Missing create permission for \u003Ctable>",{"type":53,"tag":102,"props":2312,"children":2313},{},[2314,2318],{"type":53,"tag":62,"props":2315,"children":2316},{},[2317],{"type":66,"value":1712},{"type":66,"value":2319}," Code creates records in this table but permission does not grant create access",{"type":53,"tag":102,"props":2321,"children":2322},{},[2323,2327,2328],{"type":53,"tag":62,"props":2324,"children":2325},{},[2326],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":2329,"children":2331},{"className":2330},[],[2332],{"type":66,"value":2333},"create: true",{"type":53,"tag":102,"props":2335,"children":2336},{},[2337,2338,2343,2345],{"type":66,"value":2215},{"type":53,"tag":71,"props":2339,"children":2341},{"className":2340},[],[2342],{"type":66,"value":2333},{"type":66,"value":2344}," but no create patterns in code → finding:\n",{"type":53,"tag":425,"props":2346,"children":2347},{},[2348,2361,2375,2384],{"type":53,"tag":102,"props":2349,"children":2350},{},[2351,2355,2356],{"type":53,"tag":62,"props":2352,"children":2353},{},[2354],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2357,"children":2359},{"className":2358},[],[2360],{"type":66,"value":931},{"type":53,"tag":102,"props":2362,"children":2363},{},[2364,2368,2369],{"type":53,"tag":62,"props":2365,"children":2366},{},[2367],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2370,"children":2372},{"className":2371},[],[2373],{"type":66,"value":2374},"Create enabled but not used for \u003Ctable>",{"type":53,"tag":102,"props":2376,"children":2377},{},[2378,2382],{"type":53,"tag":62,"props":2379,"children":2380},{},[2381],{"type":66,"value":1712},{"type":66,"value":2383}," No create operations found in code — permission may be overly permissive",{"type":53,"tag":102,"props":2385,"children":2386},{},[2387,2391,2393,2398],{"type":53,"tag":62,"props":2388,"children":2389},{},[2390],{"type":66,"value":1722},{"type":66,"value":2392}," Consider disabling ",{"type":53,"tag":71,"props":2394,"children":2396},{"className":2395},[],[2397],{"type":66,"value":597},{"type":66,"value":2399}," if not needed",{"type":53,"tag":102,"props":2401,"children":2402},{},[2403,2405],{"type":66,"value":2404},"If matched → ",{"type":53,"tag":71,"props":2406,"children":2408},{"className":2407},[],[2409],{"type":66,"value":1523},{"type":53,"tag":58,"props":2411,"children":2412},{},[2413],{"type":53,"tag":62,"props":2414,"children":2415},{},[2416],{"type":66,"value":2417},"F. Write Permission",{"type":53,"tag":58,"props":2419,"children":2420},{},[2421,2422,2427],{"type":66,"value":2107},{"type":53,"tag":71,"props":2423,"children":2425},{"className":2424},[],[2426],{"type":66,"value":604},{"type":66,"value":2114},{"type":53,"tag":425,"props":2429,"children":2430},{},[2431,2483,2548,2611,2680,2744],{"type":53,"tag":102,"props":2432,"children":2433},{},[2434,2436,2442,2444,2450,2452,2457,2458,2463,2464,2469,2470,2475,2476,2482],{"type":66,"value":2435},"Search the service code for PATCH\u002Fupdate\u002Fupload patterns: PATCH method usage (",{"type":53,"tag":71,"props":2437,"children":2439},{"className":2438},[],[2440],{"type":66,"value":2441},"method: 'PATCH'",{"type":66,"value":2443},"), update functions (",{"type":53,"tag":71,"props":2445,"children":2447},{"className":2446},[],[2448],{"type":66,"value":2449},"update\u003CTableName>",{"type":66,"value":2451},"), file upload patterns (",{"type":53,"tag":71,"props":2453,"children":2455},{"className":2454},[],[2456],{"type":66,"value":724},{"type":66,"value":506},{"type":53,"tag":71,"props":2459,"children":2461},{"className":2460},[],[2462],{"type":66,"value":731},{"type":66,"value":506},{"type":53,"tag":71,"props":2465,"children":2467},{"className":2466},[],[2468],{"type":66,"value":738},{"type":66,"value":506},{"type":53,"tag":71,"props":2471,"children":2473},{"className":2472},[],[2474],{"type":66,"value":745},{"type":66,"value":506},{"type":53,"tag":71,"props":2477,"children":2479},{"className":2478},[],[2480],{"type":66,"value":2481},"upload*File",{"type":66,"value":701},{"type":53,"tag":102,"props":2484,"children":2485},{},[2486,2488,2494,2495],{"type":66,"value":2487},"If code updates records but ",{"type":53,"tag":71,"props":2489,"children":2491},{"className":2490},[],[2492],{"type":66,"value":2493},"write: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":2496,"children":2497},{},[2498,2511,2525,2534],{"type":53,"tag":102,"props":2499,"children":2500},{},[2501,2505,2506],{"type":53,"tag":62,"props":2502,"children":2503},{},[2504],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2507,"children":2509},{"className":2508},[],[2510],{"type":66,"value":908},{"type":53,"tag":102,"props":2512,"children":2513},{},[2514,2518,2519],{"type":53,"tag":62,"props":2515,"children":2516},{},[2517],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2520,"children":2522},{"className":2521},[],[2523],{"type":66,"value":2524},"Missing write permission for \u003Ctable>",{"type":53,"tag":102,"props":2526,"children":2527},{},[2528,2532],{"type":53,"tag":62,"props":2529,"children":2530},{},[2531],{"type":66,"value":1712},{"type":66,"value":2533}," Code updates records (or uploads files) in this table but permission does not grant write access",{"type":53,"tag":102,"props":2535,"children":2536},{},[2537,2541,2542],{"type":53,"tag":62,"props":2538,"children":2539},{},[2540],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":2543,"children":2545},{"className":2544},[],[2546],{"type":66,"value":2547},"write: true",{"type":53,"tag":102,"props":2549,"children":2550},{},[2551,2553,2558,2559],{"type":66,"value":2552},"If file upload patterns found but ",{"type":53,"tag":71,"props":2554,"children":2556},{"className":2555},[],[2557],{"type":66,"value":2493},{"type":66,"value":1671},{"type":53,"tag":425,"props":2560,"children":2561},{},[2562,2575,2589,2598],{"type":53,"tag":102,"props":2563,"children":2564},{},[2565,2569,2570],{"type":53,"tag":62,"props":2566,"children":2567},{},[2568],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2571,"children":2573},{"className":2572},[],[2574],{"type":66,"value":917},{"type":53,"tag":102,"props":2576,"children":2577},{},[2578,2582,2583],{"type":53,"tag":62,"props":2579,"children":2580},{},[2581],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2584,"children":2586},{"className":2585},[],[2587],{"type":66,"value":2588},"File upload detected but write is disabled on \u003Ctable>",{"type":53,"tag":102,"props":2590,"children":2591},{},[2592,2596],{"type":53,"tag":62,"props":2593,"children":2594},{},[2595],{"type":66,"value":1712},{"type":66,"value":2597}," File uploads use PATCH which requires write permission",{"type":53,"tag":102,"props":2599,"children":2600},{},[2601,2605,2606],{"type":53,"tag":62,"props":2602,"children":2603},{},[2604],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":2607,"children":2609},{"className":2608},[],[2610],{"type":66,"value":2547},{"type":53,"tag":102,"props":2612,"children":2613},{},[2614,2615,2620,2622,2627,2628],{"type":66,"value":2215},{"type":53,"tag":71,"props":2616,"children":2618},{"className":2617},[],[2619],{"type":66,"value":2547},{"type":66,"value":2621}," but ",{"type":53,"tag":71,"props":2623,"children":2625},{"className":2624},[],[2626],{"type":66,"value":2155},{"type":66,"value":1671},{"type":53,"tag":425,"props":2629,"children":2630},{},[2631,2644,2658,2667],{"type":53,"tag":102,"props":2632,"children":2633},{},[2634,2638,2639],{"type":53,"tag":62,"props":2635,"children":2636},{},[2637],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2640,"children":2642},{"className":2641},[],[2643],{"type":66,"value":917},{"type":53,"tag":102,"props":2645,"children":2646},{},[2647,2651,2652],{"type":53,"tag":62,"props":2648,"children":2649},{},[2650],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2653,"children":2655},{"className":2654},[],[2656],{"type":66,"value":2657},"Write enabled without read on \u003Ctable>",{"type":53,"tag":102,"props":2659,"children":2660},{},[2661,2665],{"type":53,"tag":62,"props":2662,"children":2663},{},[2664],{"type":66,"value":1712},{"type":66,"value":2666}," Users can modify records they cannot see, which is unusual and likely unintended",{"type":53,"tag":102,"props":2668,"children":2669},{},[2670,2674,2675],{"type":53,"tag":62,"props":2671,"children":2672},{},[2673],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":2676,"children":2678},{"className":2677},[],[2679],{"type":66,"value":2210},{"type":53,"tag":102,"props":2681,"children":2682},{},[2683,2684,2689,2691],{"type":66,"value":2215},{"type":53,"tag":71,"props":2685,"children":2687},{"className":2686},[],[2688],{"type":66,"value":2547},{"type":66,"value":2690}," but no write patterns in code → finding:\n",{"type":53,"tag":425,"props":2692,"children":2693},{},[2694,2707,2721,2730],{"type":53,"tag":102,"props":2695,"children":2696},{},[2697,2701,2702],{"type":53,"tag":62,"props":2698,"children":2699},{},[2700],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2703,"children":2705},{"className":2704},[],[2706],{"type":66,"value":931},{"type":53,"tag":102,"props":2708,"children":2709},{},[2710,2714,2715],{"type":53,"tag":62,"props":2711,"children":2712},{},[2713],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2716,"children":2718},{"className":2717},[],[2719],{"type":66,"value":2720},"Write enabled but not used for \u003Ctable>",{"type":53,"tag":102,"props":2722,"children":2723},{},[2724,2728],{"type":53,"tag":62,"props":2725,"children":2726},{},[2727],{"type":66,"value":1712},{"type":66,"value":2729}," No update operations found in code — permission may be overly permissive",{"type":53,"tag":102,"props":2731,"children":2732},{},[2733,2737,2738,2743],{"type":53,"tag":62,"props":2734,"children":2735},{},[2736],{"type":66,"value":1722},{"type":66,"value":2392},{"type":53,"tag":71,"props":2739,"children":2741},{"className":2740},[],[2742],{"type":66,"value":604},{"type":66,"value":2399},{"type":53,"tag":102,"props":2745,"children":2746},{},[2747,2748],{"type":66,"value":2404},{"type":53,"tag":71,"props":2749,"children":2751},{"className":2750},[],[2752],{"type":66,"value":1523},{"type":53,"tag":58,"props":2754,"children":2755},{},[2756],{"type":53,"tag":62,"props":2757,"children":2758},{},[2759],{"type":66,"value":2760},"G. Delete Permission",{"type":53,"tag":58,"props":2762,"children":2763},{},[2764,2765,2770],{"type":66,"value":2107},{"type":53,"tag":71,"props":2766,"children":2768},{"className":2767},[],[2769],{"type":66,"value":611},{"type":66,"value":2114},{"type":53,"tag":425,"props":2772,"children":2773},{},[2774,2794,2859,2923],{"type":53,"tag":102,"props":2775,"children":2776},{},[2777,2779,2785,2787,2793],{"type":66,"value":2778},"Search the service code for DELETE patterns: DELETE method usage (",{"type":53,"tag":71,"props":2780,"children":2782},{"className":2781},[],[2783],{"type":66,"value":2784},"method: 'DELETE'",{"type":66,"value":2786},"), delete functions (",{"type":53,"tag":71,"props":2788,"children":2790},{"className":2789},[],[2791],{"type":66,"value":2792},"delete\u003CTableName>",{"type":66,"value":701},{"type":53,"tag":102,"props":2795,"children":2796},{},[2797,2799,2805,2806],{"type":66,"value":2798},"If code deletes records but ",{"type":53,"tag":71,"props":2800,"children":2802},{"className":2801},[],[2803],{"type":66,"value":2804},"delete: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":2807,"children":2808},{},[2809,2822,2836,2845],{"type":53,"tag":102,"props":2810,"children":2811},{},[2812,2816,2817],{"type":53,"tag":62,"props":2813,"children":2814},{},[2815],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2818,"children":2820},{"className":2819},[],[2821],{"type":66,"value":908},{"type":53,"tag":102,"props":2823,"children":2824},{},[2825,2829,2830],{"type":53,"tag":62,"props":2826,"children":2827},{},[2828],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2831,"children":2833},{"className":2832},[],[2834],{"type":66,"value":2835},"Missing delete permission for \u003Ctable>",{"type":53,"tag":102,"props":2837,"children":2838},{},[2839,2843],{"type":53,"tag":62,"props":2840,"children":2841},{},[2842],{"type":66,"value":1712},{"type":66,"value":2844}," Code deletes records in this table but permission does not grant delete access",{"type":53,"tag":102,"props":2846,"children":2847},{},[2848,2852,2853],{"type":53,"tag":62,"props":2849,"children":2850},{},[2851],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":2854,"children":2856},{"className":2855},[],[2857],{"type":66,"value":2858},"delete: true",{"type":53,"tag":102,"props":2860,"children":2861},{},[2862,2863,2868,2870],{"type":66,"value":2215},{"type":53,"tag":71,"props":2864,"children":2866},{"className":2865},[],[2867],{"type":66,"value":2858},{"type":66,"value":2869}," but no delete patterns in code → finding:\n",{"type":53,"tag":425,"props":2871,"children":2872},{},[2873,2886,2900,2909],{"type":53,"tag":102,"props":2874,"children":2875},{},[2876,2880,2881],{"type":53,"tag":62,"props":2877,"children":2878},{},[2879],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":2882,"children":2884},{"className":2883},[],[2885],{"type":66,"value":931},{"type":53,"tag":102,"props":2887,"children":2888},{},[2889,2893,2894],{"type":53,"tag":62,"props":2890,"children":2891},{},[2892],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":2895,"children":2897},{"className":2896},[],[2898],{"type":66,"value":2899},"Delete enabled but not used for \u003Ctable>",{"type":53,"tag":102,"props":2901,"children":2902},{},[2903,2907],{"type":53,"tag":62,"props":2904,"children":2905},{},[2906],{"type":66,"value":1712},{"type":66,"value":2908}," No delete operations found in code — permission may be overly permissive",{"type":53,"tag":102,"props":2910,"children":2911},{},[2912,2916,2917,2922],{"type":53,"tag":62,"props":2913,"children":2914},{},[2915],{"type":66,"value":1722},{"type":66,"value":2392},{"type":53,"tag":71,"props":2918,"children":2920},{"className":2919},[],[2921],{"type":66,"value":611},{"type":66,"value":2399},{"type":53,"tag":102,"props":2924,"children":2925},{},[2926,2927],{"type":66,"value":2404},{"type":53,"tag":71,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":66,"value":1523},{"type":53,"tag":58,"props":2933,"children":2934},{},[2935],{"type":53,"tag":62,"props":2936,"children":2937},{},[2938],{"type":66,"value":2939},"H. Append (target table check)",{"type":53,"tag":58,"props":2941,"children":2942},{},[2943,2945,2950,2952,2957],{"type":66,"value":2944},"Does this table need ",{"type":53,"tag":71,"props":2946,"children":2948},{"className":2947},[],[2949],{"type":66,"value":1365},{"type":66,"value":2951},"? Append is required on the ",{"type":53,"tag":62,"props":2953,"children":2954},{},[2955],{"type":66,"value":2956},"target",{"type":66,"value":2958}," table — the table that other records link TO via lookup columns.",{"type":53,"tag":425,"props":2960,"children":2961},{},[2962,2987,3006,3086,3102],{"type":53,"tag":102,"props":2963,"children":2964},{},[2965,2967,2972,2974,2979,2980,2985],{"type":66,"value":2966},"Check the ",{"type":53,"tag":62,"props":2968,"children":2969},{},[2970],{"type":66,"value":2971},"reverse target map",{"type":66,"value":2973}," from Step 3.2: is this table referenced as a lookup target by any other table that has ",{"type":53,"tag":71,"props":2975,"children":2977},{"className":2976},[],[2978],{"type":66,"value":597},{"type":66,"value":1122},{"type":53,"tag":71,"props":2981,"children":2983},{"className":2982},[],[2984],{"type":66,"value":604},{"type":66,"value":2986}," permissions?",{"type":53,"tag":102,"props":2988,"children":2989},{},[2990,2992,2997,2999,3005],{"type":66,"value":2991},"Also search the service code for ",{"type":53,"tag":71,"props":2993,"children":2995},{"className":2994},[],[2996],{"type":66,"value":712},{"type":66,"value":2998}," references to this table's entity set (e.g., ",{"type":53,"tag":71,"props":3000,"children":3002},{"className":3001},[],[3003],{"type":66,"value":3004},"\u002F\u003Centity_set>(",{"type":66,"value":701},{"type":53,"tag":102,"props":3007,"children":3008},{},[3009,3011,3017,3018],{"type":66,"value":3010},"If this table appears in the reverse target map (i.e., another table with create\u002Fwrite has a lookup to this table), but ",{"type":53,"tag":71,"props":3012,"children":3014},{"className":3013},[],[3015],{"type":66,"value":3016},"append: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":3019,"children":3020},{},[3021,3034,3048,3073],{"type":53,"tag":102,"props":3022,"children":3023},{},[3024,3028,3029],{"type":53,"tag":62,"props":3025,"children":3026},{},[3027],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":3030,"children":3032},{"className":3031},[],[3033],{"type":66,"value":908},{"type":53,"tag":102,"props":3035,"children":3036},{},[3037,3041,3042],{"type":53,"tag":62,"props":3038,"children":3039},{},[3040],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":3043,"children":3045},{"className":3044},[],[3046],{"type":66,"value":3047},"Missing append on \u003Ctable>",{"type":53,"tag":102,"props":3049,"children":3050},{},[3051,3055,3057,3063,3065,3071],{"type":53,"tag":62,"props":3052,"children":3053},{},[3054],{"type":66,"value":1712},{"type":66,"value":3056}," Table ",{"type":53,"tag":71,"props":3058,"children":3060},{"className":3059},[],[3061],{"type":66,"value":3062},"\u003Csource_table>",{"type":66,"value":3064}," has lookup column ",{"type":53,"tag":71,"props":3066,"children":3068},{"className":3067},[],[3069],{"type":66,"value":3070},"\u003Ccolumn>",{"type":66,"value":3072}," targeting this table and sets it during create\u002Fwrite. 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\"",{"type":53,"tag":102,"props":3074,"children":3075},{},[3076,3080,3081],{"type":53,"tag":62,"props":3077,"children":3078},{},[3079],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":3082,"children":3084},{"className":3083},[],[3085],{"type":66,"value":1365},{"type":53,"tag":102,"props":3087,"children":3088},{},[3089,3090,3095,3097],{"type":66,"value":2215},{"type":53,"tag":71,"props":3091,"children":3093},{"className":3092},[],[3094],{"type":66,"value":1365},{"type":66,"value":3096}," and justified → ",{"type":53,"tag":71,"props":3098,"children":3100},{"className":3099},[],[3101],{"type":66,"value":1523},{"type":53,"tag":102,"props":3103,"children":3104},{},[3105,3106,3111,3113],{"type":66,"value":2215},{"type":53,"tag":71,"props":3107,"children":3109},{"className":3108},[],[3110],{"type":66,"value":1365},{"type":66,"value":3112}," but this table does NOT appear in the reverse target map and no code references it as a lookup target → finding:\n",{"type":53,"tag":425,"props":3114,"children":3115},{},[3116,3129,3143,3152],{"type":53,"tag":102,"props":3117,"children":3118},{},[3119,3123,3124],{"type":53,"tag":62,"props":3120,"children":3121},{},[3122],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":3125,"children":3127},{"className":3126},[],[3128],{"type":66,"value":931},{"type":53,"tag":102,"props":3130,"children":3131},{},[3132,3136,3137],{"type":53,"tag":62,"props":3133,"children":3134},{},[3135],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":3138,"children":3140},{"className":3139},[],[3141],{"type":66,"value":3142},"Append enabled but not needed on \u003Ctable>",{"type":53,"tag":102,"props":3144,"children":3145},{},[3146,3150],{"type":53,"tag":62,"props":3147,"children":3148},{},[3149],{"type":66,"value":1712},{"type":66,"value":3151}," No other table with create\u002Fwrite has a lookup to this table — append may be unnecessary",{"type":53,"tag":102,"props":3153,"children":3154},{},[3155,3159,3160,3165],{"type":53,"tag":62,"props":3156,"children":3157},{},[3158],{"type":66,"value":1722},{"type":66,"value":2392},{"type":53,"tag":71,"props":3161,"children":3163},{"className":3162},[],[3164],{"type":66,"value":618},{"type":66,"value":2399},{"type":53,"tag":58,"props":3167,"children":3168},{},[3169],{"type":53,"tag":62,"props":3170,"children":3171},{},[3172],{"type":66,"value":3173},"H2. AppendTo (source table check)",{"type":53,"tag":58,"props":3175,"children":3176},{},[3177,3178,3183,3185,3190],{"type":66,"value":2944},{"type":53,"tag":71,"props":3179,"children":3181},{"className":3180},[],[3182],{"type":66,"value":1338},{"type":66,"value":3184},"? AppendTo is required on the ",{"type":53,"tag":62,"props":3186,"children":3187},{},[3188],{"type":66,"value":3189},"source",{"type":66,"value":3191}," table — the table that has lookup columns linking TO other records.",{"type":53,"tag":425,"props":3193,"children":3194},{},[3195,3206,3217,3309],{"type":53,"tag":102,"props":3196,"children":3197},{},[3198,3199,3204],{"type":66,"value":2966},{"type":53,"tag":62,"props":3200,"children":3201},{},[3202],{"type":66,"value":3203},"source map",{"type":66,"value":3205}," from Step 3.2: does this table have lookup columns?",{"type":53,"tag":102,"props":3207,"children":3208},{},[3209,3210,3215],{"type":66,"value":2991},{"type":53,"tag":71,"props":3211,"children":3213},{"className":3212},[],[3214],{"type":66,"value":712},{"type":66,"value":3216}," patterns in create\u002Fupdate calls for this table",{"type":53,"tag":102,"props":3218,"children":3219},{},[3220,3222,3227,3228,3233,3235,3241,3242],{"type":66,"value":3221},"If this table has lookup columns (in source map or code) AND has ",{"type":53,"tag":71,"props":3223,"children":3225},{"className":3224},[],[3226],{"type":66,"value":597},{"type":66,"value":1122},{"type":53,"tag":71,"props":3229,"children":3231},{"className":3230},[],[3232],{"type":66,"value":604},{"type":66,"value":3234}," enabled, but ",{"type":53,"tag":71,"props":3236,"children":3238},{"className":3237},[],[3239],{"type":66,"value":3240},"appendto: false",{"type":66,"value":1671},{"type":53,"tag":425,"props":3243,"children":3244},{},[3245,3258,3272,3296],{"type":53,"tag":102,"props":3246,"children":3247},{},[3248,3252,3253],{"type":53,"tag":62,"props":3249,"children":3250},{},[3251],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":3254,"children":3256},{"className":3255},[],[3257],{"type":66,"value":908},{"type":53,"tag":102,"props":3259,"children":3260},{},[3261,3265,3266],{"type":53,"tag":62,"props":3262,"children":3263},{},[3264],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":3267,"children":3269},{"className":3268},[],[3270],{"type":66,"value":3271},"Missing appendto on \u003Ctable>",{"type":53,"tag":102,"props":3273,"children":3274},{},[3275,3279,3281,3286,3288,3294],{"type":53,"tag":62,"props":3276,"children":3277},{},[3278],{"type":66,"value":1712},{"type":66,"value":3280}," This table sets lookup column ",{"type":53,"tag":71,"props":3282,"children":3284},{"className":3283},[],[3285],{"type":66,"value":3070},{"type":66,"value":3287}," targeting ",{"type":53,"tag":71,"props":3289,"children":3291},{"className":3290},[],[3292],{"type":66,"value":3293},"\u003Ctarget_table>",{"type":66,"value":3295}," during create\u002Fwrite, which requires appendto permission. Users will see \"You don't have permission to associate or disassociate\"",{"type":53,"tag":102,"props":3297,"children":3298},{},[3299,3303,3304],{"type":53,"tag":62,"props":3300,"children":3301},{},[3302],{"type":66,"value":1722},{"type":66,"value":2204},{"type":53,"tag":71,"props":3305,"children":3307},{"className":3306},[],[3308],{"type":66,"value":1338},{"type":53,"tag":102,"props":3310,"children":3311},{},[3312,3313,3318,3319],{"type":66,"value":2215},{"type":53,"tag":71,"props":3314,"children":3316},{"className":3315},[],[3317],{"type":66,"value":1338},{"type":66,"value":3096},{"type":53,"tag":71,"props":3320,"children":3322},{"className":3321},[],[3323],{"type":66,"value":1523},{"type":53,"tag":58,"props":3325,"children":3326},{},[3327],{"type":53,"tag":62,"props":3328,"children":3329},{},[3330],{"type":66,"value":3331},"I. Parent Chain Integrity",{"type":53,"tag":58,"props":3333,"children":3334},{},[3335,3337,3343],{"type":66,"value":3336},"If the permission has Parent scope (",{"type":53,"tag":71,"props":3338,"children":3340},{"className":3339},[],[3341],{"type":66,"value":3342},"756150003",{"type":66,"value":3344},"):",{"type":53,"tag":425,"props":3346,"children":3347},{},[3348,3360,3371,3424],{"type":53,"tag":102,"props":3349,"children":3350},{},[3351,3353,3358],{"type":66,"value":3352},"Verify ",{"type":53,"tag":71,"props":3354,"children":3356},{"className":3355},[],[3357],{"type":66,"value":665},{"type":66,"value":3359}," references a valid permission ID that exists",{"type":53,"tag":102,"props":3361,"children":3362},{},[3363,3364,3369],{"type":66,"value":3352},{"type":53,"tag":71,"props":3365,"children":3367},{"className":3366},[],[3368],{"type":66,"value":672},{"type":66,"value":3370}," is a valid Dataverse relationship (if API available, using Step 3.3 results)",{"type":53,"tag":102,"props":3372,"children":3373},{},[3374,3376],{"type":66,"value":3375},"If broken → finding:\n",{"type":53,"tag":425,"props":3377,"children":3378},{},[3379,3392,3406,3415],{"type":53,"tag":102,"props":3380,"children":3381},{},[3382,3386,3387],{"type":53,"tag":62,"props":3383,"children":3384},{},[3385],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":3388,"children":3390},{"className":3389},[],[3391],{"type":66,"value":908},{"type":53,"tag":102,"props":3393,"children":3394},{},[3395,3399,3400],{"type":53,"tag":62,"props":3396,"children":3397},{},[3398],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":3401,"children":3403},{"className":3402},[],[3404],{"type":66,"value":3405},"Broken parent chain for \u003Cpermission>",{"type":53,"tag":102,"props":3407,"children":3408},{},[3409,3413],{"type":53,"tag":62,"props":3410,"children":3411},{},[3412],{"type":66,"value":1712},{"type":66,"value":3414}," The parent permission reference is invalid — this permission will not grant any access",{"type":53,"tag":102,"props":3416,"children":3417},{},[3418,3422],{"type":53,"tag":62,"props":3419,"children":3420},{},[3421],{"type":66,"value":1722},{"type":66,"value":3423}," Correct the parent permission ID and\u002For relationship name",{"type":53,"tag":102,"props":3425,"children":3426},{},[3427,3429],{"type":66,"value":3428},"If valid → ",{"type":53,"tag":71,"props":3430,"children":3432},{"className":3431},[],[3433],{"type":66,"value":1523},{"type":53,"tag":58,"props":3435,"children":3436},{},[3437],{"type":53,"tag":62,"props":3438,"children":3439},{},[3440],{"type":66,"value":3441},"J. $expand Related Table Coverage",{"type":53,"tag":58,"props":3443,"children":3444},{},[3445,3447,3452],{"type":66,"value":3446},"Is this table fetched via ",{"type":53,"tag":71,"props":3448,"children":3450},{"className":3449},[],[3451],{"type":66,"value":755},{"type":66,"value":3453}," on another table's query?",{"type":53,"tag":425,"props":3455,"children":3456},{},[3457,3486,3574],{"type":53,"tag":102,"props":3458,"children":3459},{},[3460,3461,3466,3468,3473,3474,3479,3480,3485],{"type":66,"value":2966},{"type":53,"tag":71,"props":3462,"children":3464},{"className":3463},[],[3465],{"type":66,"value":755},{"type":66,"value":3467}," analysis from Step 2.3 (search site source code for ",{"type":53,"tag":71,"props":3469,"children":3471},{"className":3470},[],[3472],{"type":66,"value":755},{"type":66,"value":506},{"type":53,"tag":71,"props":3475,"children":3477},{"className":3476},[],[3478],{"type":66,"value":769},{"type":66,"value":506},{"type":53,"tag":71,"props":3481,"children":3483},{"className":3482},[],[3484],{"type":66,"value":776},{"type":66,"value":701},{"type":53,"tag":102,"props":3487,"children":3488},{},[3489,3491,3496,3498],{"type":66,"value":3490},"If this table is expanded from another table but has no table permission with ",{"type":53,"tag":71,"props":3492,"children":3494},{"className":3493},[],[3495],{"type":66,"value":2210},{"type":66,"value":3497}," for the same web role → finding:\n",{"type":53,"tag":425,"props":3499,"children":3500},{},[3501,3514,3528,3558],{"type":53,"tag":102,"props":3502,"children":3503},{},[3504,3508,3509],{"type":53,"tag":62,"props":3505,"children":3506},{},[3507],{"type":66,"value":1682},{"type":66,"value":1684},{"type":53,"tag":71,"props":3510,"children":3512},{"className":3511},[],[3513],{"type":66,"value":908},{"type":53,"tag":102,"props":3515,"children":3516},{},[3517,3521,3522],{"type":53,"tag":62,"props":3518,"children":3519},{},[3520],{"type":66,"value":1697},{"type":66,"value":1684},{"type":53,"tag":71,"props":3523,"children":3525},{"className":3524},[],[3526],{"type":66,"value":3527},"Missing read permission for expanded table \u003Ctable>",{"type":53,"tag":102,"props":3529,"children":3530},{},[3531,3535,3537,3542,3544,3549,3550,3556],{"type":53,"tag":62,"props":3532,"children":3533},{},[3534],{"type":66,"value":1712},{"type":66,"value":3536}," This table is fetched via ",{"type":53,"tag":71,"props":3538,"children":3540},{"className":3539},[],[3541],{"type":66,"value":755},{"type":66,"value":3543}," on ",{"type":53,"tag":71,"props":3545,"children":3547},{"className":3546},[],[3548],{"type":66,"value":1442},{"type":66,"value":1352},{"type":53,"tag":71,"props":3551,"children":3553},{"className":3552},[],[3554],{"type":66,"value":3555},"\u003Cservice_file>",{"type":66,"value":3557},", but has no read permission. Power Pages enforces table permissions on every entity in the query.",{"type":53,"tag":102,"props":3559,"children":3560},{},[3561,3565,3567,3572],{"type":53,"tag":62,"props":3562,"children":3563},{},[3564],{"type":66,"value":1722},{"type":66,"value":3566}," Create a table permission with ",{"type":53,"tag":71,"props":3568,"children":3570},{"className":3569},[],[3571],{"type":66,"value":2210},{"type":66,"value":3573}," 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.",{"type":53,"tag":102,"props":3575,"children":3576},{},[3577,3579],{"type":66,"value":3578},"If properly covered → ",{"type":53,"tag":71,"props":3580,"children":3582},{"className":3581},[],[3583],{"type":66,"value":1523},{"type":53,"tag":58,"props":3585,"children":3586},{},[3587],{"type":53,"tag":62,"props":3588,"children":3589},{},[3590],{"type":66,"value":3591},"K. Record Findings & Complete",{"type":53,"tag":58,"props":3593,"children":3594},{},[3595,3597,3602,3604,3610],{"type":66,"value":3596},"After all checks, mark the table's task as ",{"type":53,"tag":71,"props":3598,"children":3600},{"className":3599},[],[3601],{"type":66,"value":222},{"type":66,"value":3603}," via ",{"type":53,"tag":71,"props":3605,"children":3607},{"className":3606},[],[3608],{"type":66,"value":3609},"TaskUpdate",{"type":66,"value":1525},{"type":53,"tag":480,"props":3612,"children":3614},{"id":3613},"_44-cross-table-validation",[3615],{"type":66,"value":3616},"4.4 Cross-Table Validation",{"type":53,"tag":58,"props":3618,"children":3619},{},[3620],{"type":66,"value":3621},"After all per-table audits are complete, run these cross-table checks:",{"type":53,"tag":98,"props":3623,"children":3624},{},[3625,3663,3685,3695],{"type":53,"tag":102,"props":3626,"children":3627},{},[3628,3633,3635,3640,3642,3647,3649,3654,3656,3661],{"type":53,"tag":62,"props":3629,"children":3630},{},[3631],{"type":66,"value":3632},"Append\u002FAppendTo consistency:",{"type":66,"value":3634}," Using the source map and reverse target map from Step 3.2, verify: (a) every source table (with lookups and create\u002Fwrite) has ",{"type":53,"tag":71,"props":3636,"children":3638},{"className":3637},[],[3639],{"type":66,"value":1338},{"type":66,"value":3641},", (b) every target table in the reverse map has ",{"type":53,"tag":71,"props":3643,"children":3645},{"className":3644},[],[3646],{"type":66,"value":1365},{"type":66,"value":3648},", (c) no table has ",{"type":53,"tag":71,"props":3650,"children":3652},{"className":3651},[],[3653],{"type":66,"value":1338},{"type":66,"value":3655}," without lookup columns in the source map, (d) no table has ",{"type":53,"tag":71,"props":3657,"children":3659},{"className":3658},[],[3660],{"type":66,"value":1365},{"type":66,"value":3662}," without being in the reverse target map",{"type":53,"tag":102,"props":3664,"children":3665},{},[3666,3671,3673,3678,3680],{"type":53,"tag":62,"props":3667,"children":3668},{},[3669],{"type":66,"value":3670},"$expand coverage:",{"type":66,"value":3672}," For every ",{"type":53,"tag":71,"props":3674,"children":3676},{"className":3675},[],[3677],{"type":66,"value":755},{"type":66,"value":3679}," usage, verify the expanded table has ",{"type":53,"tag":71,"props":3681,"children":3683},{"className":3682},[],[3684],{"type":66,"value":2210},{"type":53,"tag":102,"props":3686,"children":3687},{},[3688,3693],{"type":53,"tag":62,"props":3689,"children":3690},{},[3691],{"type":66,"value":3692},"Parent chain completeness:",{"type":66,"value":3694}," For every Parent scope permission, verify the parent permission exists and is valid",{"type":53,"tag":102,"props":3696,"children":3697},{},[3698,3703],{"type":53,"tag":62,"props":3699,"children":3700},{},[3701],{"type":66,"value":3702},"Web role consistency:",{"type":66,"value":3704}," If two related tables (e.g., parent and child) are accessed by the same feature, verify they share the same web role assignment",{"type":53,"tag":58,"props":3706,"children":3707},{},[3708,3709,3714,3716,3721],{"type":66,"value":415},{"type":53,"tag":71,"props":3710,"children":3712},{"className":3711},[],[3713],{"type":66,"value":1609},{"type":66,"value":3715}," to review all completed audits, then mark the \"Compile audit findings\" task as ",{"type":53,"tag":71,"props":3717,"children":3719},{"className":3718},[],[3720],{"type":66,"value":214},{"type":66,"value":3722}," and proceed to Step 5.",{"type":53,"tag":402,"props":3724,"children":3725},{},[],{"type":53,"tag":91,"props":3727,"children":3729},{"id":3728},"step-5-generate-report",[3730],{"type":66,"value":3731},"Step 5: Generate Report",{"type":53,"tag":480,"props":3733,"children":3735},{"id":3734},"_51-determine-output-location",[3736],{"type":66,"value":3737},"5.1 Determine Output Location",{"type":53,"tag":425,"props":3739,"children":3740},{},[3741,3765],{"type":53,"tag":102,"props":3742,"children":3743},{},[3744,3749,3751,3757,3759],{"type":53,"tag":62,"props":3745,"children":3746},{},[3747],{"type":66,"value":3748},"If working in context of a website",{"type":66,"value":3750}," (project root with ",{"type":53,"tag":71,"props":3752,"children":3754},{"className":3753},[],[3755],{"type":66,"value":3756},"powerpages.config.json",{"type":66,"value":3758}," exists): write to ",{"type":53,"tag":71,"props":3760,"children":3762},{"className":3761},[],[3763],{"type":66,"value":3764},"\u003CPROJECT_ROOT>\u002Fdocs\u002Fpermissions-audit.html",{"type":53,"tag":102,"props":3766,"children":3767},{},[3768,3773],{"type":53,"tag":62,"props":3769,"children":3770},{},[3771],{"type":66,"value":3772},"Otherwise",{"type":66,"value":3774},": write to the system temp directory",{"type":53,"tag":480,"props":3776,"children":3778},{"id":3777},"_52-prepare-data",[3779],{"type":66,"value":3780},"5.2 Prepare Data",{"type":53,"tag":58,"props":3782,"children":3783},{},[3784,3789,3791,3797],{"type":53,"tag":62,"props":3785,"children":3786},{},[3787],{"type":66,"value":3788},"Do NOT generate HTML manually or read\u002Fmodify the template yourself.",{"type":66,"value":3790}," Use the ",{"type":53,"tag":71,"props":3792,"children":3794},{"className":3793},[],[3795],{"type":66,"value":3796},"render-audit-report.js",{"type":66,"value":3798}," script which mechanically reads the template and replaces placeholder tokens with your data.",{"type":53,"tag":58,"props":3800,"children":3801},{},[3802,3804,3810],{"type":66,"value":3803},"Write a temporary JSON data file (e.g., ",{"type":53,"tag":71,"props":3805,"children":3807},{"className":3806},[],[3808],{"type":66,"value":3809},"\u003COUTPUT_DIR>\u002Faudit-data.json",{"type":66,"value":3811},") with these keys:",{"type":53,"tag":815,"props":3813,"children":3817},{"className":3814,"code":3815,"language":3816,"meta":820,"style":820},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"SITE_NAME\": \"The site name (from powerpages.config.json or folder name)\",\n  \"AUDIT_DESC\": \"Security audit of table permissions for Contoso Portal\",\n  \"SUMMARY\": \"2-3 sentence summary of the audit results\",\n  \"FINDINGS_DATA\": [\u002F* array of finding objects *\u002F],\n  \"INVENTORY_DATA\": [\u002F* array of current permission objects *\u002F]\n}\n","json",[3818],{"type":53,"tag":71,"props":3819,"children":3820},{"__ignoreMap":820},[3821,3829,3871,3909,3947,3984,4019],{"type":53,"tag":826,"props":3822,"children":3823},{"class":828,"line":829},[3824],{"type":53,"tag":826,"props":3825,"children":3826},{"style":839},[3827],{"type":66,"value":3828},"{\n",{"type":53,"tag":826,"props":3830,"children":3832},{"class":828,"line":3831},2,[3833,3838,3844,3848,3853,3857,3862,3866],{"type":53,"tag":826,"props":3834,"children":3835},{"style":839},[3836],{"type":66,"value":3837},"  \"",{"type":53,"tag":826,"props":3839,"children":3841},{"style":3840},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[3842],{"type":66,"value":3843},"SITE_NAME",{"type":53,"tag":826,"props":3845,"children":3846},{"style":839},[3847],{"type":66,"value":864},{"type":53,"tag":826,"props":3849,"children":3850},{"style":839},[3851],{"type":66,"value":3852},":",{"type":53,"tag":826,"props":3854,"children":3855},{"style":839},[3856],{"type":66,"value":874},{"type":53,"tag":826,"props":3858,"children":3859},{"style":856},[3860],{"type":66,"value":3861},"The site name (from powerpages.config.json or folder name)",{"type":53,"tag":826,"props":3863,"children":3864},{"style":839},[3865],{"type":66,"value":864},{"type":53,"tag":826,"props":3867,"children":3868},{"style":839},[3869],{"type":66,"value":3870},",\n",{"type":53,"tag":826,"props":3872,"children":3874},{"class":828,"line":3873},3,[3875,3879,3884,3888,3892,3896,3901,3905],{"type":53,"tag":826,"props":3876,"children":3877},{"style":839},[3878],{"type":66,"value":3837},{"type":53,"tag":826,"props":3880,"children":3881},{"style":3840},[3882],{"type":66,"value":3883},"AUDIT_DESC",{"type":53,"tag":826,"props":3885,"children":3886},{"style":839},[3887],{"type":66,"value":864},{"type":53,"tag":826,"props":3889,"children":3890},{"style":839},[3891],{"type":66,"value":3852},{"type":53,"tag":826,"props":3893,"children":3894},{"style":839},[3895],{"type":66,"value":874},{"type":53,"tag":826,"props":3897,"children":3898},{"style":856},[3899],{"type":66,"value":3900},"Security audit of table permissions for Contoso Portal",{"type":53,"tag":826,"props":3902,"children":3903},{"style":839},[3904],{"type":66,"value":864},{"type":53,"tag":826,"props":3906,"children":3907},{"style":839},[3908],{"type":66,"value":3870},{"type":53,"tag":826,"props":3910,"children":3912},{"class":828,"line":3911},4,[3913,3917,3922,3926,3930,3934,3939,3943],{"type":53,"tag":826,"props":3914,"children":3915},{"style":839},[3916],{"type":66,"value":3837},{"type":53,"tag":826,"props":3918,"children":3919},{"style":3840},[3920],{"type":66,"value":3921},"SUMMARY",{"type":53,"tag":826,"props":3923,"children":3924},{"style":839},[3925],{"type":66,"value":864},{"type":53,"tag":826,"props":3927,"children":3928},{"style":839},[3929],{"type":66,"value":3852},{"type":53,"tag":826,"props":3931,"children":3932},{"style":839},[3933],{"type":66,"value":874},{"type":53,"tag":826,"props":3935,"children":3936},{"style":856},[3937],{"type":66,"value":3938},"2-3 sentence summary of the audit results",{"type":53,"tag":826,"props":3940,"children":3941},{"style":839},[3942],{"type":66,"value":864},{"type":53,"tag":826,"props":3944,"children":3945},{"style":839},[3946],{"type":66,"value":3870},{"type":53,"tag":826,"props":3948,"children":3950},{"class":828,"line":3949},5,[3951,3955,3960,3964,3968,3973,3979],{"type":53,"tag":826,"props":3952,"children":3953},{"style":839},[3954],{"type":66,"value":3837},{"type":53,"tag":826,"props":3956,"children":3957},{"style":3840},[3958],{"type":66,"value":3959},"FINDINGS_DATA",{"type":53,"tag":826,"props":3961,"children":3962},{"style":839},[3963],{"type":66,"value":864},{"type":53,"tag":826,"props":3965,"children":3966},{"style":839},[3967],{"type":66,"value":3852},{"type":53,"tag":826,"props":3969,"children":3970},{"style":839},[3971],{"type":66,"value":3972}," [",{"type":53,"tag":826,"props":3974,"children":3976},{"style":3975},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[3977],{"type":66,"value":3978},"\u002F* array of finding objects *\u002F",{"type":53,"tag":826,"props":3980,"children":3981},{"style":839},[3982],{"type":66,"value":3983},"],\n",{"type":53,"tag":826,"props":3985,"children":3987},{"class":828,"line":3986},6,[3988,3992,3997,4001,4005,4009,4014],{"type":53,"tag":826,"props":3989,"children":3990},{"style":839},[3991],{"type":66,"value":3837},{"type":53,"tag":826,"props":3993,"children":3994},{"style":3840},[3995],{"type":66,"value":3996},"INVENTORY_DATA",{"type":53,"tag":826,"props":3998,"children":3999},{"style":839},[4000],{"type":66,"value":864},{"type":53,"tag":826,"props":4002,"children":4003},{"style":839},[4004],{"type":66,"value":3852},{"type":53,"tag":826,"props":4006,"children":4007},{"style":839},[4008],{"type":66,"value":3972},{"type":53,"tag":826,"props":4010,"children":4011},{"style":3975},[4012],{"type":66,"value":4013},"\u002F* array of current permission objects *\u002F",{"type":53,"tag":826,"props":4015,"children":4016},{"style":839},[4017],{"type":66,"value":4018},"]\n",{"type":53,"tag":826,"props":4020,"children":4022},{"class":828,"line":4021},7,[4023],{"type":53,"tag":826,"props":4024,"children":4025},{"style":839},[4026],{"type":66,"value":4027},"}\n",{"type":53,"tag":58,"props":4029,"children":4030},{},[4031],{"type":53,"tag":62,"props":4032,"children":4033},{},[4034],{"type":66,"value":4035},"FINDINGS_DATA format:",{"type":53,"tag":815,"props":4037,"children":4039},{"className":3814,"code":4038,"language":3816,"meta":820,"style":820},"{\n  \"id\": \"f1\",\n  \"severity\": \"critical\",\n  \"title\": \"Missing permission for cra5b_product\",\n  \"table\": \"cra5b_product\",\n  \"scope\": null,\n  \"permission\": null,\n  \"reasoning\": \"The table cra5b_product is referenced in src\u002Fservices\u002FproductService.ts with GET requests to \u002F_api\u002Fcra5b_products, but no table permission exists for this table.\",\n  \"fix\": \"Create a table permission with Global scope and read-only access for the Anonymous Users role.\",\n  \"details\": \"Referenced in: src\u002Fservices\u002FproductService.ts (line 23), src\u002Fcomponents\u002FProductList.tsx (line 45)\"\n}\n",[4040],{"type":53,"tag":71,"props":4041,"children":4042},{"__ignoreMap":820},[4043,4050,4086,4122,4159,4195,4219,4243,4281,4319,4353],{"type":53,"tag":826,"props":4044,"children":4045},{"class":828,"line":829},[4046],{"type":53,"tag":826,"props":4047,"children":4048},{"style":839},[4049],{"type":66,"value":3828},{"type":53,"tag":826,"props":4051,"children":4052},{"class":828,"line":3831},[4053,4057,4061,4065,4069,4073,4078,4082],{"type":53,"tag":826,"props":4054,"children":4055},{"style":839},[4056],{"type":66,"value":3837},{"type":53,"tag":826,"props":4058,"children":4059},{"style":3840},[4060],{"type":66,"value":504},{"type":53,"tag":826,"props":4062,"children":4063},{"style":839},[4064],{"type":66,"value":864},{"type":53,"tag":826,"props":4066,"children":4067},{"style":839},[4068],{"type":66,"value":3852},{"type":53,"tag":826,"props":4070,"children":4071},{"style":839},[4072],{"type":66,"value":874},{"type":53,"tag":826,"props":4074,"children":4075},{"style":856},[4076],{"type":66,"value":4077},"f1",{"type":53,"tag":826,"props":4079,"children":4080},{"style":839},[4081],{"type":66,"value":864},{"type":53,"tag":826,"props":4083,"children":4084},{"style":839},[4085],{"type":66,"value":3870},{"type":53,"tag":826,"props":4087,"children":4088},{"class":828,"line":3873},[4089,4093,4098,4102,4106,4110,4114,4118],{"type":53,"tag":826,"props":4090,"children":4091},{"style":839},[4092],{"type":66,"value":3837},{"type":53,"tag":826,"props":4094,"children":4095},{"style":3840},[4096],{"type":66,"value":4097},"severity",{"type":53,"tag":826,"props":4099,"children":4100},{"style":839},[4101],{"type":66,"value":864},{"type":53,"tag":826,"props":4103,"children":4104},{"style":839},[4105],{"type":66,"value":3852},{"type":53,"tag":826,"props":4107,"children":4108},{"style":839},[4109],{"type":66,"value":874},{"type":53,"tag":826,"props":4111,"children":4112},{"style":856},[4113],{"type":66,"value":908},{"type":53,"tag":826,"props":4115,"children":4116},{"style":839},[4117],{"type":66,"value":864},{"type":53,"tag":826,"props":4119,"children":4120},{"style":839},[4121],{"type":66,"value":3870},{"type":53,"tag":826,"props":4123,"children":4124},{"class":828,"line":3911},[4125,4129,4134,4138,4142,4146,4151,4155],{"type":53,"tag":826,"props":4126,"children":4127},{"style":839},[4128],{"type":66,"value":3837},{"type":53,"tag":826,"props":4130,"children":4131},{"style":3840},[4132],{"type":66,"value":4133},"title",{"type":53,"tag":826,"props":4135,"children":4136},{"style":839},[4137],{"type":66,"value":864},{"type":53,"tag":826,"props":4139,"children":4140},{"style":839},[4141],{"type":66,"value":3852},{"type":53,"tag":826,"props":4143,"children":4144},{"style":839},[4145],{"type":66,"value":874},{"type":53,"tag":826,"props":4147,"children":4148},{"style":856},[4149],{"type":66,"value":4150},"Missing permission for cra5b_product",{"type":53,"tag":826,"props":4152,"children":4153},{"style":839},[4154],{"type":66,"value":864},{"type":53,"tag":826,"props":4156,"children":4157},{"style":839},[4158],{"type":66,"value":3870},{"type":53,"tag":826,"props":4160,"children":4161},{"class":828,"line":3949},[4162,4166,4170,4174,4178,4182,4187,4191],{"type":53,"tag":826,"props":4163,"children":4164},{"style":839},[4165],{"type":66,"value":3837},{"type":53,"tag":826,"props":4167,"children":4168},{"style":3840},[4169],{"type":66,"value":226},{"type":53,"tag":826,"props":4171,"children":4172},{"style":839},[4173],{"type":66,"value":864},{"type":53,"tag":826,"props":4175,"children":4176},{"style":839},[4177],{"type":66,"value":3852},{"type":53,"tag":826,"props":4179,"children":4180},{"style":839},[4181],{"type":66,"value":874},{"type":53,"tag":826,"props":4183,"children":4184},{"style":856},[4185],{"type":66,"value":4186},"cra5b_product",{"type":53,"tag":826,"props":4188,"children":4189},{"style":839},[4190],{"type":66,"value":864},{"type":53,"tag":826,"props":4192,"children":4193},{"style":839},[4194],{"type":66,"value":3870},{"type":53,"tag":826,"props":4196,"children":4197},{"class":828,"line":3986},[4198,4202,4206,4210,4214],{"type":53,"tag":826,"props":4199,"children":4200},{"style":839},[4201],{"type":66,"value":3837},{"type":53,"tag":826,"props":4203,"children":4204},{"style":3840},[4205],{"type":66,"value":579},{"type":53,"tag":826,"props":4207,"children":4208},{"style":839},[4209],{"type":66,"value":864},{"type":53,"tag":826,"props":4211,"children":4212},{"style":839},[4213],{"type":66,"value":3852},{"type":53,"tag":826,"props":4215,"children":4216},{"style":839},[4217],{"type":66,"value":4218}," null,\n",{"type":53,"tag":826,"props":4220,"children":4221},{"class":828,"line":4021},[4222,4226,4231,4235,4239],{"type":53,"tag":826,"props":4223,"children":4224},{"style":839},[4225],{"type":66,"value":3837},{"type":53,"tag":826,"props":4227,"children":4228},{"style":3840},[4229],{"type":66,"value":4230},"permission",{"type":53,"tag":826,"props":4232,"children":4233},{"style":839},[4234],{"type":66,"value":864},{"type":53,"tag":826,"props":4236,"children":4237},{"style":839},[4238],{"type":66,"value":3852},{"type":53,"tag":826,"props":4240,"children":4241},{"style":839},[4242],{"type":66,"value":4218},{"type":53,"tag":826,"props":4244,"children":4246},{"class":828,"line":4245},8,[4247,4251,4256,4260,4264,4268,4273,4277],{"type":53,"tag":826,"props":4248,"children":4249},{"style":839},[4250],{"type":66,"value":3837},{"type":53,"tag":826,"props":4252,"children":4253},{"style":3840},[4254],{"type":66,"value":4255},"reasoning",{"type":53,"tag":826,"props":4257,"children":4258},{"style":839},[4259],{"type":66,"value":864},{"type":53,"tag":826,"props":4261,"children":4262},{"style":839},[4263],{"type":66,"value":3852},{"type":53,"tag":826,"props":4265,"children":4266},{"style":839},[4267],{"type":66,"value":874},{"type":53,"tag":826,"props":4269,"children":4270},{"style":856},[4271],{"type":66,"value":4272},"The table cra5b_product is referenced in src\u002Fservices\u002FproductService.ts with GET requests to \u002F_api\u002Fcra5b_products, but no table permission exists for this table.",{"type":53,"tag":826,"props":4274,"children":4275},{"style":839},[4276],{"type":66,"value":864},{"type":53,"tag":826,"props":4278,"children":4279},{"style":839},[4280],{"type":66,"value":3870},{"type":53,"tag":826,"props":4282,"children":4284},{"class":828,"line":4283},9,[4285,4289,4294,4298,4302,4306,4311,4315],{"type":53,"tag":826,"props":4286,"children":4287},{"style":839},[4288],{"type":66,"value":3837},{"type":53,"tag":826,"props":4290,"children":4291},{"style":3840},[4292],{"type":66,"value":4293},"fix",{"type":53,"tag":826,"props":4295,"children":4296},{"style":839},[4297],{"type":66,"value":864},{"type":53,"tag":826,"props":4299,"children":4300},{"style":839},[4301],{"type":66,"value":3852},{"type":53,"tag":826,"props":4303,"children":4304},{"style":839},[4305],{"type":66,"value":874},{"type":53,"tag":826,"props":4307,"children":4308},{"style":856},[4309],{"type":66,"value":4310},"Create a table permission with Global scope and read-only access for the Anonymous Users role.",{"type":53,"tag":826,"props":4312,"children":4313},{"style":839},[4314],{"type":66,"value":864},{"type":53,"tag":826,"props":4316,"children":4317},{"style":839},[4318],{"type":66,"value":3870},{"type":53,"tag":826,"props":4320,"children":4322},{"class":828,"line":4321},10,[4323,4327,4332,4336,4340,4344,4349],{"type":53,"tag":826,"props":4324,"children":4325},{"style":839},[4326],{"type":66,"value":3837},{"type":53,"tag":826,"props":4328,"children":4329},{"style":3840},[4330],{"type":66,"value":4331},"details",{"type":53,"tag":826,"props":4333,"children":4334},{"style":839},[4335],{"type":66,"value":864},{"type":53,"tag":826,"props":4337,"children":4338},{"style":839},[4339],{"type":66,"value":3852},{"type":53,"tag":826,"props":4341,"children":4342},{"style":839},[4343],{"type":66,"value":874},{"type":53,"tag":826,"props":4345,"children":4346},{"style":856},[4347],{"type":66,"value":4348},"Referenced in: src\u002Fservices\u002FproductService.ts (line 23), src\u002Fcomponents\u002FProductList.tsx (line 45)",{"type":53,"tag":826,"props":4350,"children":4351},{"style":839},[4352],{"type":66,"value":884},{"type":53,"tag":826,"props":4354,"children":4356},{"class":828,"line":4355},11,[4357],{"type":53,"tag":826,"props":4358,"children":4359},{"style":839},[4360],{"type":66,"value":4027},{"type":53,"tag":425,"props":4362,"children":4363},{},[4364,4397,4415,4430,4445,4455,4479],{"type":53,"tag":102,"props":4365,"children":4366},{},[4367,4372,4374,4379,4380,4385,4386,4391,4392],{"type":53,"tag":71,"props":4368,"children":4370},{"className":4369},[],[4371],{"type":66,"value":4097},{"type":66,"value":4373},": One of ",{"type":53,"tag":71,"props":4375,"children":4377},{"className":4376},[],[4378],{"type":66,"value":908},{"type":66,"value":506},{"type":53,"tag":71,"props":4381,"children":4383},{"className":4382},[],[4384],{"type":66,"value":917},{"type":66,"value":506},{"type":53,"tag":71,"props":4387,"children":4389},{"className":4388},[],[4390],{"type":66,"value":931},{"type":66,"value":506},{"type":53,"tag":71,"props":4393,"children":4395},{"className":4394},[],[4396],{"type":66,"value":1523},{"type":53,"tag":102,"props":4398,"children":4399},{},[4400,4405,4407,4413],{"type":53,"tag":71,"props":4401,"children":4403},{"className":4402},[],[4404],{"type":66,"value":226},{"type":66,"value":4406},": The table logical name this finding relates to (or ",{"type":53,"tag":71,"props":4408,"children":4410},{"className":4409},[],[4411],{"type":66,"value":4412},"null",{"type":66,"value":4414}," for general findings)",{"type":53,"tag":102,"props":4416,"children":4417},{},[4418,4423,4425],{"type":53,"tag":71,"props":4419,"children":4421},{"className":4420},[],[4422],{"type":66,"value":579},{"type":66,"value":4424},": The current scope if applicable (numeric code or friendly name), or ",{"type":53,"tag":71,"props":4426,"children":4428},{"className":4427},[],[4429],{"type":66,"value":4412},{"type":53,"tag":102,"props":4431,"children":4432},{},[4433,4438,4440],{"type":53,"tag":71,"props":4434,"children":4436},{"className":4435},[],[4437],{"type":66,"value":4230},{"type":66,"value":4439},": The permission name if this finding is about an existing permission, or ",{"type":53,"tag":71,"props":4441,"children":4443},{"className":4442},[],[4444],{"type":66,"value":4412},{"type":53,"tag":102,"props":4446,"children":4447},{},[4448,4453],{"type":53,"tag":71,"props":4449,"children":4451},{"className":4450},[],[4452],{"type":66,"value":4255},{"type":66,"value":4454},": Detailed explanation of why this is an issue — reference specific code files, line patterns, or Dataverse metadata",{"type":53,"tag":102,"props":4456,"children":4457},{},[4458,4463,4465,4470,4472,4477],{"type":53,"tag":71,"props":4459,"children":4461},{"className":4460},[],[4462],{"type":66,"value":4293},{"type":66,"value":4464},": Actionable suggestion for how to resolve the issue (or ",{"type":53,"tag":71,"props":4466,"children":4468},{"className":4467},[],[4469],{"type":66,"value":4412},{"type":66,"value":4471}," for ",{"type":53,"tag":71,"props":4473,"children":4475},{"className":4474},[],[4476],{"type":66,"value":1523},{"type":66,"value":4478}," findings)",{"type":53,"tag":102,"props":4480,"children":4481},{},[4482,4487],{"type":53,"tag":71,"props":4483,"children":4485},{"className":4484},[],[4486],{"type":66,"value":4331},{"type":66,"value":4488},": Additional context like file references, column names, or relationship details",{"type":53,"tag":58,"props":4490,"children":4491},{},[4492],{"type":53,"tag":62,"props":4493,"children":4494},{},[4495],{"type":66,"value":4496},"INVENTORY_DATA format:",{"type":53,"tag":815,"props":4498,"children":4500},{"className":3814,"code":4499,"language":3816,"meta":820,"style":820},"{\n  \"name\": \"Product - Anonymous Read\",\n  \"table\": \"cra5b_product\",\n  \"scope\": \"Global\",\n  \"roles\": [\"Anonymous Users\"],\n  \"read\": true,\n  \"create\": false,\n  \"write\": false,\n  \"delete\": false,\n  \"append\": true,\n  \"appendto\": false\n}\n",[4501],{"type":53,"tag":71,"props":4502,"children":4503},{"__ignoreMap":820},[4504,4511,4547,4582,4618,4659,4683,4707,4730,4753,4776,4800],{"type":53,"tag":826,"props":4505,"children":4506},{"class":828,"line":829},[4507],{"type":53,"tag":826,"props":4508,"children":4509},{"style":839},[4510],{"type":66,"value":3828},{"type":53,"tag":826,"props":4512,"children":4513},{"class":828,"line":3831},[4514,4518,4522,4526,4530,4534,4539,4543],{"type":53,"tag":826,"props":4515,"children":4516},{"style":839},[4517],{"type":66,"value":3837},{"type":53,"tag":826,"props":4519,"children":4520},{"style":3840},[4521],{"type":66,"value":512},{"type":53,"tag":826,"props":4523,"children":4524},{"style":839},[4525],{"type":66,"value":864},{"type":53,"tag":826,"props":4527,"children":4528},{"style":839},[4529],{"type":66,"value":3852},{"type":53,"tag":826,"props":4531,"children":4532},{"style":839},[4533],{"type":66,"value":874},{"type":53,"tag":826,"props":4535,"children":4536},{"style":856},[4537],{"type":66,"value":4538},"Product - Anonymous Read",{"type":53,"tag":826,"props":4540,"children":4541},{"style":839},[4542],{"type":66,"value":864},{"type":53,"tag":826,"props":4544,"children":4545},{"style":839},[4546],{"type":66,"value":3870},{"type":53,"tag":826,"props":4548,"children":4549},{"class":828,"line":3873},[4550,4554,4558,4562,4566,4570,4574,4578],{"type":53,"tag":826,"props":4551,"children":4552},{"style":839},[4553],{"type":66,"value":3837},{"type":53,"tag":826,"props":4555,"children":4556},{"style":3840},[4557],{"type":66,"value":226},{"type":53,"tag":826,"props":4559,"children":4560},{"style":839},[4561],{"type":66,"value":864},{"type":53,"tag":826,"props":4563,"children":4564},{"style":839},[4565],{"type":66,"value":3852},{"type":53,"tag":826,"props":4567,"children":4568},{"style":839},[4569],{"type":66,"value":874},{"type":53,"tag":826,"props":4571,"children":4572},{"style":856},[4573],{"type":66,"value":4186},{"type":53,"tag":826,"props":4575,"children":4576},{"style":839},[4577],{"type":66,"value":864},{"type":53,"tag":826,"props":4579,"children":4580},{"style":839},[4581],{"type":66,"value":3870},{"type":53,"tag":826,"props":4583,"children":4584},{"class":828,"line":3911},[4585,4589,4593,4597,4601,4605,4610,4614],{"type":53,"tag":826,"props":4586,"children":4587},{"style":839},[4588],{"type":66,"value":3837},{"type":53,"tag":826,"props":4590,"children":4591},{"style":3840},[4592],{"type":66,"value":579},{"type":53,"tag":826,"props":4594,"children":4595},{"style":839},[4596],{"type":66,"value":864},{"type":53,"tag":826,"props":4598,"children":4599},{"style":839},[4600],{"type":66,"value":3852},{"type":53,"tag":826,"props":4602,"children":4603},{"style":839},[4604],{"type":66,"value":874},{"type":53,"tag":826,"props":4606,"children":4607},{"style":856},[4608],{"type":66,"value":4609},"Global",{"type":53,"tag":826,"props":4611,"children":4612},{"style":839},[4613],{"type":66,"value":864},{"type":53,"tag":826,"props":4615,"children":4616},{"style":839},[4617],{"type":66,"value":3870},{"type":53,"tag":826,"props":4619,"children":4620},{"class":828,"line":3949},[4621,4625,4630,4634,4638,4642,4646,4651,4655],{"type":53,"tag":826,"props":4622,"children":4623},{"style":839},[4624],{"type":66,"value":3837},{"type":53,"tag":826,"props":4626,"children":4627},{"style":3840},[4628],{"type":66,"value":4629},"roles",{"type":53,"tag":826,"props":4631,"children":4632},{"style":839},[4633],{"type":66,"value":864},{"type":53,"tag":826,"props":4635,"children":4636},{"style":839},[4637],{"type":66,"value":3852},{"type":53,"tag":826,"props":4639,"children":4640},{"style":839},[4641],{"type":66,"value":3972},{"type":53,"tag":826,"props":4643,"children":4644},{"style":839},[4645],{"type":66,"value":864},{"type":53,"tag":826,"props":4647,"children":4648},{"style":856},[4649],{"type":66,"value":4650},"Anonymous Users",{"type":53,"tag":826,"props":4652,"children":4653},{"style":839},[4654],{"type":66,"value":864},{"type":53,"tag":826,"props":4656,"children":4657},{"style":839},[4658],{"type":66,"value":3983},{"type":53,"tag":826,"props":4660,"children":4661},{"class":828,"line":3986},[4662,4666,4670,4674,4678],{"type":53,"tag":826,"props":4663,"children":4664},{"style":839},[4665],{"type":66,"value":3837},{"type":53,"tag":826,"props":4667,"children":4668},{"style":3840},[4669],{"type":66,"value":590},{"type":53,"tag":826,"props":4671,"children":4672},{"style":839},[4673],{"type":66,"value":864},{"type":53,"tag":826,"props":4675,"children":4676},{"style":839},[4677],{"type":66,"value":3852},{"type":53,"tag":826,"props":4679,"children":4680},{"style":839},[4681],{"type":66,"value":4682}," true,\n",{"type":53,"tag":826,"props":4684,"children":4685},{"class":828,"line":4021},[4686,4690,4694,4698,4702],{"type":53,"tag":826,"props":4687,"children":4688},{"style":839},[4689],{"type":66,"value":3837},{"type":53,"tag":826,"props":4691,"children":4692},{"style":3840},[4693],{"type":66,"value":597},{"type":53,"tag":826,"props":4695,"children":4696},{"style":839},[4697],{"type":66,"value":864},{"type":53,"tag":826,"props":4699,"children":4700},{"style":839},[4701],{"type":66,"value":3852},{"type":53,"tag":826,"props":4703,"children":4704},{"style":839},[4705],{"type":66,"value":4706}," false,\n",{"type":53,"tag":826,"props":4708,"children":4709},{"class":828,"line":4245},[4710,4714,4718,4722,4726],{"type":53,"tag":826,"props":4711,"children":4712},{"style":839},[4713],{"type":66,"value":3837},{"type":53,"tag":826,"props":4715,"children":4716},{"style":3840},[4717],{"type":66,"value":604},{"type":53,"tag":826,"props":4719,"children":4720},{"style":839},[4721],{"type":66,"value":864},{"type":53,"tag":826,"props":4723,"children":4724},{"style":839},[4725],{"type":66,"value":3852},{"type":53,"tag":826,"props":4727,"children":4728},{"style":839},[4729],{"type":66,"value":4706},{"type":53,"tag":826,"props":4731,"children":4732},{"class":828,"line":4283},[4733,4737,4741,4745,4749],{"type":53,"tag":826,"props":4734,"children":4735},{"style":839},[4736],{"type":66,"value":3837},{"type":53,"tag":826,"props":4738,"children":4739},{"style":3840},[4740],{"type":66,"value":611},{"type":53,"tag":826,"props":4742,"children":4743},{"style":839},[4744],{"type":66,"value":864},{"type":53,"tag":826,"props":4746,"children":4747},{"style":839},[4748],{"type":66,"value":3852},{"type":53,"tag":826,"props":4750,"children":4751},{"style":839},[4752],{"type":66,"value":4706},{"type":53,"tag":826,"props":4754,"children":4755},{"class":828,"line":4321},[4756,4760,4764,4768,4772],{"type":53,"tag":826,"props":4757,"children":4758},{"style":839},[4759],{"type":66,"value":3837},{"type":53,"tag":826,"props":4761,"children":4762},{"style":3840},[4763],{"type":66,"value":618},{"type":53,"tag":826,"props":4765,"children":4766},{"style":839},[4767],{"type":66,"value":864},{"type":53,"tag":826,"props":4769,"children":4770},{"style":839},[4771],{"type":66,"value":3852},{"type":53,"tag":826,"props":4773,"children":4774},{"style":839},[4775],{"type":66,"value":4682},{"type":53,"tag":826,"props":4777,"children":4778},{"class":828,"line":4355},[4779,4783,4787,4791,4795],{"type":53,"tag":826,"props":4780,"children":4781},{"style":839},[4782],{"type":66,"value":3837},{"type":53,"tag":826,"props":4784,"children":4785},{"style":3840},[4786],{"type":66,"value":625},{"type":53,"tag":826,"props":4788,"children":4789},{"style":839},[4790],{"type":66,"value":864},{"type":53,"tag":826,"props":4792,"children":4793},{"style":839},[4794],{"type":66,"value":3852},{"type":53,"tag":826,"props":4796,"children":4797},{"style":839},[4798],{"type":66,"value":4799}," false\n",{"type":53,"tag":826,"props":4801,"children":4803},{"class":828,"line":4802},12,[4804],{"type":53,"tag":826,"props":4805,"children":4806},{"style":839},[4807],{"type":66,"value":4027},{"type":53,"tag":480,"props":4809,"children":4811},{"id":4810},"_53-render-the-html-file",[4812],{"type":66,"value":4813},"5.3 Render the HTML File",{"type":53,"tag":58,"props":4815,"children":4816},{},[4817],{"type":66,"value":4818},"Run the render script (it creates the output directory if needed):",{"type":53,"tag":815,"props":4820,"children":4822},{"className":817,"code":4821,"language":819,"meta":820,"style":820},"node \"${PLUGIN_ROOT}\u002Fscripts\u002Frender-audit-report.js\" --output \"\u003COUTPUT_PATH>\" --data \"\u003CDATA_JSON_PATH>\"\n",[4823],{"type":53,"tag":71,"props":4824,"children":4825},{"__ignoreMap":820},[4826],{"type":53,"tag":826,"props":4827,"children":4828},{"class":828,"line":829},[4829,4833,4837,4841,4845,4850,4854,4859,4863,4868,4872,4877,4881,4886],{"type":53,"tag":826,"props":4830,"children":4831},{"style":833},[4832],{"type":66,"value":836},{"type":53,"tag":826,"props":4834,"children":4835},{"style":839},[4836],{"type":66,"value":842},{"type":53,"tag":826,"props":4838,"children":4839},{"style":845},[4840],{"type":66,"value":848},{"type":53,"tag":826,"props":4842,"children":4843},{"style":839},[4844],{"type":66,"value":853},{"type":53,"tag":826,"props":4846,"children":4847},{"style":856},[4848],{"type":66,"value":4849},"\u002Fscripts\u002Frender-audit-report.js",{"type":53,"tag":826,"props":4851,"children":4852},{"style":839},[4853],{"type":66,"value":864},{"type":53,"tag":826,"props":4855,"children":4856},{"style":856},[4857],{"type":66,"value":4858}," --output",{"type":53,"tag":826,"props":4860,"children":4861},{"style":839},[4862],{"type":66,"value":874},{"type":53,"tag":826,"props":4864,"children":4865},{"style":856},[4866],{"type":66,"value":4867},"\u003COUTPUT_PATH>",{"type":53,"tag":826,"props":4869,"children":4870},{"style":839},[4871],{"type":66,"value":864},{"type":53,"tag":826,"props":4873,"children":4874},{"style":856},[4875],{"type":66,"value":4876}," --data",{"type":53,"tag":826,"props":4878,"children":4879},{"style":839},[4880],{"type":66,"value":874},{"type":53,"tag":826,"props":4882,"children":4883},{"style":856},[4884],{"type":66,"value":4885},"\u003CDATA_JSON_PATH>",{"type":53,"tag":826,"props":4887,"children":4888},{"style":839},[4889],{"type":66,"value":884},{"type":53,"tag":58,"props":4891,"children":4892},{},[4893,4895,4900,4902,4908,4909,4915,4917,4923],{"type":66,"value":4894},"The render script refuses to overwrite existing files. Before calling it, check if the default output path (",{"type":53,"tag":71,"props":4896,"children":4898},{"className":4897},[],[4899],{"type":66,"value":3764},{"type":66,"value":4901},") already exists. If it does, choose a new descriptive filename based on context — e.g., ",{"type":53,"tag":71,"props":4903,"children":4905},{"className":4904},[],[4906],{"type":66,"value":4907},"permissions-audit-apr-2026.html",{"type":66,"value":506},{"type":53,"tag":71,"props":4910,"children":4912},{"className":4911},[],[4913],{"type":66,"value":4914},"permissions-audit-post-migration.html",{"type":66,"value":4916},". Pass the chosen name via ",{"type":53,"tag":71,"props":4918,"children":4920},{"className":4919},[],[4921],{"type":66,"value":4922},"--output",{"type":66,"value":1525},{"type":53,"tag":58,"props":4925,"children":4926},{},[4927],{"type":66,"value":4928},"Delete the temporary data JSON file after the script succeeds.",{"type":53,"tag":480,"props":4930,"children":4932},{"id":4931},"_54-open-in-browser",[4933],{"type":66,"value":4934},"5.4 Open in Browser",{"type":53,"tag":58,"props":4936,"children":4937},{},[4938],{"type":66,"value":4939},"Open the actual output path in the user's default browser.",{"type":53,"tag":402,"props":4941,"children":4942},{},[],{"type":53,"tag":91,"props":4944,"children":4946},{"id":4945},"step-6-present-findings-track",[4947],{"type":66,"value":4948},"Step 6: Present Findings & Track",{"type":53,"tag":480,"props":4950,"children":4952},{"id":4951},"_61-record-skill-usage",[4953],{"type":66,"value":4954},"6.1 Record Skill Usage",{"type":53,"tag":54,"props":4956,"children":4957},{},[4958],{"type":53,"tag":58,"props":4959,"children":4960},{},[4961,4963],{"type":66,"value":4962},"Reference: ",{"type":53,"tag":71,"props":4964,"children":4966},{"className":4965},[],[4967],{"type":66,"value":4968},"${PLUGIN_ROOT}\u002Freferences\u002Fskill-tracking-reference.md",{"type":53,"tag":58,"props":4970,"children":4971},{},[4972,4974,4980],{"type":66,"value":4973},"Follow the skill tracking instructions in the reference to record this skill's usage. Use ",{"type":53,"tag":71,"props":4975,"children":4977},{"className":4976},[],[4978],{"type":66,"value":4979},"--skillName \"AuditPermissions\"",{"type":66,"value":1525},{"type":53,"tag":480,"props":4982,"children":4984},{"id":4983},"_62-present-summary",[4985],{"type":66,"value":4986},"6.2 Present Summary",{"type":53,"tag":54,"props":4988,"children":4989},{},[4990,5002],{"type":53,"tag":58,"props":4991,"children":4992},{},[4993,4995,5000],{"type":66,"value":4994},"🚦 ",{"type":53,"tag":62,"props":4996,"children":4997},{},[4998],{"type":66,"value":4999},"Gate (plan · audit-permissions:6.fix-offer):",{"type":66,"value":5001}," Offer to apply auto-fixes for critical\u002Fwarning findings. The audit report has already been written; declining here just leaves the HTML report in place — no Dataverse \u002F filesystem mutation.",{"type":53,"tag":58,"props":5003,"children":5004},{},[5005,5010,5012,5017,5019,5024],{"type":53,"tag":62,"props":5006,"children":5007},{},[5008],{"type":66,"value":5009},"Trigger:",{"type":66,"value":5011}," Phase 6 has tallied findings and the HTML report is saved.\n",{"type":53,"tag":62,"props":5013,"children":5014},{},[5015],{"type":66,"value":5016},"Why we ask:",{"type":66,"value":5018}," Tooling could silently invoke the table-permissions-architect agent — accept-by-default would write or mutate permission YAML against the user's intent.\n",{"type":53,"tag":62,"props":5020,"children":5021},{},[5022],{"type":66,"value":5023},"Cancel leaves:",{"type":66,"value":5025}," Nothing — the audit report stays at its saved path. No web-role \u002F table-permission files written.",{"type":53,"tag":58,"props":5027,"children":5028},{},[5029],{"type":66,"value":5030},"Present a summary to the user:",{"type":53,"tag":98,"props":5032,"children":5033},{},[5034,5044,5054,5064],{"type":53,"tag":102,"props":5035,"children":5036},{},[5037,5042],{"type":53,"tag":62,"props":5038,"children":5039},{},[5040],{"type":66,"value":5041},"Critical findings count",{"type":66,"value":5043}," — these need immediate attention",{"type":53,"tag":102,"props":5045,"children":5046},{},[5047,5052],{"type":53,"tag":62,"props":5048,"children":5049},{},[5050],{"type":66,"value":5051},"Warning findings count",{"type":66,"value":5053}," — should be addressed",{"type":53,"tag":102,"props":5055,"children":5056},{},[5057,5062],{"type":53,"tag":62,"props":5058,"children":5059},{},[5060],{"type":66,"value":5061},"Report location",{"type":66,"value":5063}," — where the HTML file was saved",{"type":53,"tag":102,"props":5065,"children":5066},{},[5067,5072,5074,5080],{"type":53,"tag":62,"props":5068,"children":5069},{},[5070],{"type":66,"value":5071},"Ask the user",{"type":66,"value":5073}," using ",{"type":53,"tag":71,"props":5075,"children":5077},{"className":5076},[],[5078],{"type":66,"value":5079},"AskUserQuestion",{"type":66,"value":5081},": \"Would you like me to fix any of these issues? I can create or update table permissions to resolve the critical and warning findings.\"",{"type":53,"tag":58,"props":5083,"children":5084},{},[5085],{"type":66,"value":5086},"If the user wants fixes applied:",{"type":53,"tag":425,"props":5088,"children":5089},{},[5090,5128],{"type":53,"tag":102,"props":5091,"children":5092},{},[5093,5098,5100,5105,5107,5112,5114,5120,5122],{"type":53,"tag":62,"props":5094,"children":5095},{},[5096],{"type":66,"value":5097},"For 403 \u002F Web API access issues",{"type":66,"value":5099}," (missing table permissions, missing CRUD flags, incorrect scope, missing append\u002Fappendto, missing ",{"type":53,"tag":71,"props":5101,"children":5103},{"className":5102},[],[5104],{"type":66,"value":755},{"type":66,"value":5106}," read coverage — any finding that would result in a 403 Forbidden response from the Power Pages Web API): Spawn the ",{"type":53,"tag":62,"props":5108,"children":5109},{},[5110],{"type":66,"value":5111},"table-permissions-architect",{"type":66,"value":5113}," agent using the ",{"type":53,"tag":71,"props":5115,"children":5117},{"className":5116},[],[5118],{"type":66,"value":5119},"Agent",{"type":66,"value":5121}," 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: ",{"type":53,"tag":71,"props":5123,"children":5125},{"className":5124},[],[5126],{"type":66,"value":5127},"\"Create table permissions for the following tables based on audit findings: \u003Ctable1> needs Global scope with read:true; \u003Ctable2> needs Parent scope under \u003Cparent_table> with read:true, create:true, append:true; \u003Ctable3> needs appendto:true for lookups from \u003Csource_table>. The site project root is \u003CPROJECT_ROOT>.\"",{"type":53,"tag":102,"props":5129,"children":5130},{},[5131,5136,5138,5144],{"type":53,"tag":62,"props":5132,"children":5133},{},[5134],{"type":66,"value":5135},"For non-permission issues",{"type":66,"value":5137}," (e.g., unused permissions, scope narrowing suggestions, informational findings): explain what manual changes are needed or suggest running ",{"type":53,"tag":71,"props":5139,"children":5141},{"className":5140},[],[5142],{"type":66,"value":5143},"\u002Fintegrate-webapi",{"type":66,"value":5145}," so the Web API settings architect can address site-setting-level issues.",{"type":53,"tag":402,"props":5147,"children":5148},{},[],{"type":53,"tag":91,"props":5150,"children":5152},{"id":5151},"critical-constraints",[5153],{"type":66,"value":5154},"Critical Constraints",{"type":53,"tag":425,"props":5156,"children":5157},{},[5158,5168,5201,5211,5227],{"type":53,"tag":102,"props":5159,"children":5160},{},[5161,5166],{"type":53,"tag":62,"props":5162,"children":5163},{},[5164],{"type":66,"value":5165},"Read-only analysis",{"type":66,"value":5167},": This skill only reads existing configuration and code. It does NOT modify any files unless the user explicitly asks to fix issues.",{"type":53,"tag":102,"props":5169,"children":5170},{},[5171,5176,5178,5184,5185,5191,5193,5199],{"type":53,"tag":62,"props":5172,"children":5173},{},[5174],{"type":66,"value":5175},"Deterministic API calls",{"type":66,"value":5177},": Always use the Node.js scripts (",{"type":53,"tag":71,"props":5179,"children":5181},{"className":5180},[],[5182],{"type":66,"value":5183},"query-table-lookups.js",{"type":66,"value":506},{"type":53,"tag":71,"props":5186,"children":5188},{"className":5187},[],[5189],{"type":66,"value":5190},"query-table-relationships.js",{"type":66,"value":5192},") for Dataverse API queries — never use inline PowerShell ",{"type":53,"tag":71,"props":5194,"children":5196},{"className":5195},[],[5197],{"type":66,"value":5198},"Invoke-RestMethod",{"type":66,"value":5200}," calls.",{"type":53,"tag":102,"props":5202,"children":5203},{},[5204,5209],{"type":53,"tag":62,"props":5205,"children":5206},{},[5207],{"type":66,"value":5208},"No questions during analysis",{"type":66,"value":5210},": Autonomously gather all data, run checks, and present findings. Only ask the user at the end about fixing issues.",{"type":53,"tag":102,"props":5212,"children":5213},{},[5214,5218,5220,5226],{"type":53,"tag":62,"props":5215,"children":5216},{},[5217],{"type":66,"value":13},{"type":66,"value":5219},": Never log or display auth tokens. The scripts handle token acquisition internally via ",{"type":53,"tag":71,"props":5221,"children":5223},{"className":5222},[],[5224],{"type":66,"value":5225},"getAuthToken()",{"type":66,"value":1525},{"type":53,"tag":102,"props":5228,"children":5229},{},[5230,5235],{"type":53,"tag":62,"props":5231,"children":5232},{},[5233],{"type":66,"value":5234},"Graceful degradation",{"type":66,"value":5236},": If Dataverse API scripts fail (exit code 1), skip API-dependent checks (H\u002FH2 append\u002Fappendto validation, I parent chain integrity) and note in the report which checks were skipped.",{"type":53,"tag":5238,"props":5239,"children":5240},"style",{},[5241],{"type":66,"value":5242},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":5244,"total":5341},[5245,5257,5271,5281,5296,5309,5325],{"slug":5246,"name":5246,"fn":5247,"description":5248,"org":5249,"tags":5250,"stars":26,"repoUrl":27,"updatedAt":5256},"activate-site","provision and activate Power Pages sites","Activates and provisions a Power Pages website in a Power Platform environment via the Power Platform REST API. Use when the user wants to activate, provision, turn on, or enable a Power Pages website or portal.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5251,5254,5255],{"name":5252,"slug":5253,"type":15},"Deployment","deployment",{"name":9,"slug":8,"type":15},{"name":20,"slug":21,"type":15},"2026-04-06T18:34:34.732549",{"slug":5258,"name":5258,"fn":5259,"description":5260,"org":5261,"tags":5262,"stars":26,"repoUrl":27,"updatedAt":5270},"add-connector","add Power Platform connectors to apps","Adds any Power Platform connector to a Power Apps code app. Generic fallback for connectors not covered by a specific skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5263,5266,5268],{"name":5264,"slug":5265,"type":15},"Integrations","integrations",{"name":5267,"slug":36,"type":15},"Power Apps",{"name":5269,"slug":37,"type":15},"Power Platform","2026-07-31T05:54:47.042251",{"slug":5272,"name":5272,"fn":5273,"description":5274,"org":5275,"tags":5276,"stars":26,"repoUrl":27,"updatedAt":5280},"add-datasource","add data sources to Power Apps","Adds a data source or connector to a Power Apps code app. Asks what the user wants to accomplish and routes to the appropriate specialized skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5277,5278,5279],{"name":5264,"slug":5265,"type":15},{"name":9,"slug":8,"type":15},{"name":5267,"slug":36,"type":15},"2026-07-03T16:31:47.822186",{"slug":5282,"name":5282,"fn":5283,"description":5284,"org":5285,"tags":5286,"stars":26,"repoUrl":27,"updatedAt":5295},"add-dataverse","add Dataverse tables to Power Apps","Use when the user wants to add Dataverse tables (existing or new) to a Power Apps mobile app, extend an existing Dataverse table with new columns, or apply an approved data model plan.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5287,5290,5293,5294],{"name":5288,"slug":5289,"type":15},"Data Modeling","data-modeling",{"name":5291,"slug":5292,"type":15},"Dataverse","dataverse",{"name":9,"slug":8,"type":15},{"name":5267,"slug":36,"type":15},"2026-07-31T05:54:46.078014",{"slug":5297,"name":5297,"fn":5298,"description":5299,"org":5300,"tags":5301,"stars":26,"repoUrl":27,"updatedAt":5308},"add-excel","integrate Excel Online into Power Apps","Adds Excel Online (Business) connector to a Power Apps code app. Use when reading or writing Excel workbook data from OneDrive or SharePoint.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5302,5305,5306,5307],{"name":5303,"slug":5304,"type":15},"Excel","excel",{"name":5264,"slug":5265,"type":15},{"name":9,"slug":8,"type":15},{"name":5267,"slug":36,"type":15},"2026-07-31T05:54:44.030943",{"slug":5310,"name":5310,"fn":5311,"description":5312,"org":5313,"tags":5314,"stars":26,"repoUrl":27,"updatedAt":5324},"add-mcscopilot","add Copilot Studio connectors to Power Apps","Adds Microsoft Copilot Studio connector to a Power Apps code app. Use when invoking Copilot Studio agents, sending prompts to agents, or integrating agent responses.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5315,5318,5321,5322,5323],{"name":5316,"slug":5317,"type":15},"Agents","agents",{"name":5319,"slug":5320,"type":15},"Copilot Studio","copilot-studio",{"name":5264,"slug":5265,"type":15},{"name":9,"slug":8,"type":15},{"name":5267,"slug":36,"type":15},"2026-07-31T05:54:39.025597",{"slug":5326,"name":5326,"fn":5327,"description":5328,"org":5329,"tags":5330,"stars":26,"repoUrl":27,"updatedAt":5340},"add-sample-data","populate Power Pages tables with sample data","Populates Dataverse tables with sample records for testing and demoing a Power Pages site. Use when the user wants to add sample data, seed data, generate test records, or insert demo data into their tables.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5331,5334,5335,5336,5337],{"name":5332,"slug":5333,"type":15},"Database","database",{"name":5291,"slug":5292,"type":15},{"name":9,"slug":8,"type":15},{"name":20,"slug":21,"type":15},{"name":5338,"slug":5339,"type":15},"Testing","testing","2026-04-06T18:34:41.141155",26,{"items":5343,"total":5532},[5344,5366,5385,5406,5419,5436,5447,5460,5475,5490,5507,5520],{"slug":5345,"name":5345,"fn":5346,"description":5347,"org":5348,"tags":5349,"stars":5363,"repoUrl":5364,"updatedAt":5365},"rushstack-best-practices","manage Rush monorepos with best practices","Provides best practices and guidance for working with Rush monorepos. Use when the user is working in a Rush-based repository, asks about Rush commands (install, update, build, rebuild), needs help with project selection, dependency management, build caching, subspace configuration, or troubleshooting Rush-specific issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5350,5353,5356,5357,5360],{"name":5351,"slug":5352,"type":15},"Engineering","engineering",{"name":5354,"slug":5355,"type":15},"Local Development","local-development",{"name":9,"slug":8,"type":15},{"name":5358,"slug":5359,"type":15},"Project Management","project-management",{"name":5361,"slug":5362,"type":15},"Rush","rush",6484,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Frushstack","2026-04-06T18:34:44.965032",{"slug":5367,"name":5367,"fn":5368,"description":5369,"org":5370,"tags":5371,"stars":5382,"repoUrl":5383,"updatedAt":5384},"azure-ai-agents-persistent-dotnet","build AI agents with Azure .NET SDK","Azure AI Agents Persistent SDK for .NET. Low-level SDK for creating and managing AI agents with threads, messages, runs, and tools. Use for agent CRUD, conversation threads, streaming responses, function calling, file search, and code interpreter. Triggers: \"PersistentAgentsClient\", \"persistent agents\", \"agent threads\", \"agent runs\", \"streaming agents\", \"function calling agents .NET\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5372,5375,5376,5379],{"name":5373,"slug":5374,"type":15},".NET","net",{"name":5316,"slug":5317,"type":15},{"name":5377,"slug":5378,"type":15},"Azure","azure",{"name":5380,"slug":5381,"type":15},"LLM","llm",2804,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fskills","2026-07-03T16:32:10.297433",{"slug":5386,"name":5386,"fn":5387,"description":5388,"org":5389,"tags":5390,"stars":5382,"repoUrl":5383,"updatedAt":5405},"azure-ai-anomalydetector-java","build anomaly detection applications with Java","Build anomaly detection applications with Azure AI Anomaly Detector SDK for Java. Use when implementing univariate\u002Fmultivariate anomaly detection, time-series analysis, or AI-powered monitoring.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5391,5394,5395,5398,5401,5402],{"name":5392,"slug":5393,"type":15},"Analytics","analytics",{"name":5377,"slug":5378,"type":15},{"name":5396,"slug":5397,"type":15},"Data Analysis","data-analysis",{"name":5399,"slug":5400,"type":15},"Java","java",{"name":9,"slug":8,"type":15},{"name":5403,"slug":5404,"type":15},"Monitoring","monitoring","2026-05-13T06:14:16.261754",{"slug":5407,"name":5407,"fn":5408,"description":5409,"org":5410,"tags":5411,"stars":5382,"repoUrl":5383,"updatedAt":5418},"azure-ai-contentsafety-java","build content moderation applications with Azure AI","Build content moderation applications with Azure AI Content Safety SDK for Java. Use when implementing text\u002Fimage analysis, blocklist management, or harm detection for hate, violence, sexual content, and self-harm.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5412,5415,5416,5417],{"name":5413,"slug":5414,"type":15},"AI Infrastructure","ai-infrastructure",{"name":5377,"slug":5378,"type":15},{"name":5399,"slug":5400,"type":15},{"name":13,"slug":14,"type":15},"2026-07-07T06:53:31.293235",{"slug":5420,"name":5420,"fn":5421,"description":5422,"org":5423,"tags":5424,"stars":5382,"repoUrl":5383,"updatedAt":5435},"azure-ai-contentsafety-py","detect harmful content with Azure AI Content Safety","Azure AI Content Safety SDK for Python. Use for detecting harmful content in text and images with multi-severity classification.\nTriggers: \"azure-ai-contentsafety\", \"ContentSafetyClient\", \"content moderation\", \"harmful content\", \"text analysis\", \"image analysis\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5425,5426,5429,5430,5431,5434],{"name":5377,"slug":5378,"type":15},{"name":5427,"slug":5428,"type":15},"Compliance","compliance",{"name":5380,"slug":5381,"type":15},{"name":9,"slug":8,"type":15},{"name":5432,"slug":5433,"type":15},"Python","python",{"name":13,"slug":14,"type":15},"2026-07-18T05:14:23.017504",{"slug":5437,"name":5437,"fn":5438,"description":5439,"org":5440,"tags":5441,"stars":5382,"repoUrl":5383,"updatedAt":5446},"azure-ai-language-conversations-py","implement conversational language understanding with Python","Implement Conversational Language Understanding (CLU) using the azure-ai-language-conversations Python SDK. Use when working with ConversationAnalysisClient to analyze conversation intent and entities, building NLP features, or integrating language understanding into applications.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5442,5443,5444,5445],{"name":5392,"slug":5393,"type":15},{"name":5377,"slug":5378,"type":15},{"name":5380,"slug":5381,"type":15},{"name":5432,"slug":5433,"type":15},"2026-07-31T05:54:29.068751",{"slug":5448,"name":5448,"fn":5449,"description":5450,"org":5451,"tags":5452,"stars":5382,"repoUrl":5383,"updatedAt":5459},"azure-ai-translation-text-py","translate text using Azure AI services","Azure AI Text Translation SDK for real-time text translation, transliteration, language detection, and dictionary lookup. Use for translating text content in applications.\nTriggers: \"text translation\", \"translator\", \"translate text\", \"transliterate\", \"TextTranslationClient\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5453,5456,5457,5458],{"name":5454,"slug":5455,"type":15},"API Development","api-development",{"name":5377,"slug":5378,"type":15},{"name":9,"slug":8,"type":15},{"name":5432,"slug":5433,"type":15},"2026-07-18T05:14:16.988376",{"slug":5461,"name":5461,"fn":5462,"description":5463,"org":5464,"tags":5465,"stars":5382,"repoUrl":5383,"updatedAt":5474},"azure-ai-vision-imageanalysis-py","analyze images with Azure AI Vision","Azure AI Vision Image Analysis SDK for captions, tags, objects, OCR, people detection, and smart cropping. Use for computer vision and image understanding tasks.\nTriggers: \"image analysis\", \"computer vision\", \"OCR\", \"object detection\", \"ImageAnalysisClient\", \"image caption\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5466,5467,5470,5473],{"name":5377,"slug":5378,"type":15},{"name":5468,"slug":5469,"type":15},"Computer Vision","computer-vision",{"name":5471,"slug":5472,"type":15},"Images","images",{"name":5432,"slug":5433,"type":15},"2026-07-18T05:14:18.007737",{"slug":5476,"name":5476,"fn":5477,"description":5478,"org":5479,"tags":5480,"stars":5382,"repoUrl":5383,"updatedAt":5489},"azure-appconfiguration-java","manage configuration with Azure App Configuration","Azure App Configuration SDK for Java. Centralized application configuration management with key-value settings, feature flags, and snapshots.\nTriggers: \"ConfigurationClient java\", \"app configuration java\", \"feature flag java\", \"configuration setting java\", \"azure config java\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5481,5482,5485,5488],{"name":5377,"slug":5378,"type":15},{"name":5483,"slug":5484,"type":15},"Configuration","configuration",{"name":5486,"slug":5487,"type":15},"Feature Flags","feature-flags",{"name":5399,"slug":5400,"type":15},"2026-07-03T16:32:01.278468",{"slug":5491,"name":5491,"fn":5492,"description":5493,"org":5494,"tags":5495,"stars":5382,"repoUrl":5383,"updatedAt":5506},"azure-cosmos-rust","build applications with Azure Cosmos DB","Azure Cosmos DB library for Rust (NoSQL API). Document CRUD, containers, and globally distributed data.\nTriggers: \"cosmos db rust\", \"CosmosClient rust\", \"document crud rust\", \"NoSQL rust\", \"partition key rust\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5496,5499,5500,5503],{"name":5497,"slug":5498,"type":15},"Cosmos DB","cosmos-db",{"name":5332,"slug":5333,"type":15},{"name":5501,"slug":5502,"type":15},"NoSQL","nosql",{"name":5504,"slug":5505,"type":15},"Rust","rust","2026-07-31T05:54:27.021432",{"slug":5508,"name":5508,"fn":5492,"description":5509,"org":5510,"tags":5511,"stars":5382,"repoUrl":5383,"updatedAt":5519},"azure-cosmos-ts","Azure Cosmos DB JavaScript\u002FTypeScript SDK (@azure\u002Fcosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: \"Cosmos DB\", \"@azure\u002Fcosmos\", \"CosmosClient\", \"document CRUD\", \"NoSQL queries\", \"bulk operations\", \"partition key\", \"container.items\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5512,5513,5514,5515,5516],{"name":5497,"slug":5498,"type":15},{"name":5332,"slug":5333,"type":15},{"name":9,"slug":8,"type":15},{"name":5501,"slug":5502,"type":15},{"name":5517,"slug":5518,"type":15},"TypeScript","typescript","2026-07-03T16:31:19.368382",{"slug":5521,"name":5521,"fn":5522,"description":5523,"org":5524,"tags":5525,"stars":5382,"repoUrl":5383,"updatedAt":5531},"azure-data-tables-java","build table storage applications with Java","Build table storage applications with Azure Tables SDK for Java. Use when working with Azure Table Storage or Cosmos DB Table API for NoSQL key-value data, schemaless storage, or structured data at scale.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[5526,5527,5528,5529,5530],{"name":5377,"slug":5378,"type":15},{"name":5497,"slug":5498,"type":15},{"name":5332,"slug":5333,"type":15},{"name":5399,"slug":5400,"type":15},{"name":5501,"slug":5502,"type":15},"2026-05-13T06:14:17.582229",267]