scan-site
Scan-site runs security scans on deployed Power Pages sites and retrieves findings in plain-language summaries. Use this skill when you need to audit a live Power Pages site for security vulnerabilities. The scan executes server-side and can take minutes to hours depending on site size. It resolves the site via website record ID, checks for running scans, optionally starts a new scan with polling, fetches the latest report, and surfaces results with context-aware recommendations for remediation.
git clone --depth 1 https://github.com/microsoft/power-platform-skills /tmp/scan-site && cp -r /tmp/scan-site/plugins/power-pages/skills/scan-site ~/.claude/skills/scan-siteSKILL.md
> **Plugin check**: Run `node "${PLUGIN_ROOT}/scripts/check-version.js"` — if it outputs a message, show it to the user before proceeding.
# Scan Site
Run a security scan on a deployed Power Pages site, fetch the latest scan report, and surface findings in a plain-language summary. The scan runs server-side; duration depends on site size — small sites finish in minutes, large sites can take hours.
This skill scans the live deployed site, not local source code.
**Initial request:** $ARGUMENTS
## Gotchas
- **Website record id vs portal id.** `.powerpages-site/website.yml` stores the website record id, not the portal id. Every script takes `--portalId`. Resolve once via `website.js --websiteId` during prerequisites.
- **Never resolve by name.** Site names can duplicate inside an environment; only the website record id is safe.
- **`null` from the resolver** means the site is not deployed, or the authenticated profile points at a different environment.
- **Scans are long-running.** Duration depends on site size — small sites finish in minutes, large sites can take hours. Poll in the background and increase `--timeoutMinutes` for large sites.
- **Only one scan per site at a time.** A start while a scan is running returns `Z003` — `start-deep-scan.js` reports it as `{ "status": "already-running" }` (exit 0).
- **Rate limits may apply.** The service may throttle repeated scans on the same site. When throttled, wait and retry later.
- **No completed scan yet.** A fresh site or a site mid-scan has no completed report — `get-latest-report.js` returns `{ "status": "empty" }`.
## Workflow
1. **Prerequisites** — Locate project, confirm sign-in, identify site
2. **Check scan state** — Detect whether a scan is currently running
3. **Choose an action** — Context-aware recommendation (run new scan / show latest)
4. **Run the scan** — Start and poll for completion
5. **Fetch and summarize** — Get the report, present findings
6. **Walk through follow-ups** — Route issues to the right downstream skill (only if the report contains issues)
## Task Tracking
Create tasks in four groups. Mark each `in_progress` when starting, `completed` when done.
| Group | When to create | Tasks |
|-------|----------------|-------|
| 1 | At start | Check prerequisites |
| 2 | After prerequisites pass | Check scan state · Choose an action (skip in review mode) |
| 3 | After user confirms an action (or in review mode) | Run the scan (skip only if the user chose to view latest results in interactive mode) · Fetch and summarize (always) |
| 4 | After fetch and summarize | Walk through follow-ups (only if the report contains issues AND not in review mode) |
---
## 1. Prerequisites
### 1.1 Locate the project, detect review mode
Use `Glob` to find `**/powerpages.config.json`. If `$ARGUMENTS` contains `--review <out-dir>`, remember the output directory — Step 3 (choose an action) is skipped, Step 4 (run scan) executes automatically (start a fresh scan or attach to a running one), Step 5 writes JSON only, and Step 6 (follow-ups) is skipped.
### 1.2 Resolve site identifiers
Read `.powerpages-site/website.yml` → extract `id` field → that is `<WEBSITE_ID>`.
If missing, the site has not been deployed. Tell the user and recommend `/deploy-site`. Stop. Do **not** resolve by name or URL.
Resolve to portalId:
```bash
node "${PLUGIN_ROOT}/scripts/website.js" --websiteId "<WEBSITE_ID>"
```
Capture `Id` (portalId), `Type`, `Name`, `WebsiteUrl`. If exit code `2` → sign-in required (`pac auth create` or `az login`). If `null` → site not found in this environment. Stop in either case.
---
## 2. Check scan state
```bash
node "${PLUGIN_ROOT}/skills/scan-site/scripts/poll-deep-scan.js" --portalId "<PORTAL_ID>" --once
```
`--once` does a single status check, exits 0, and prints:
- `{ "status": "ongoing" }` → a scan is currently running.
- `{ "status": "idle" }` → no scan running.
Then call `get-latest-report.js` to know whether a completed report exists:
```bash
node "${PLUGIN_ROOT}/skills/scan-site/scripts/get-latest-report.js" --portalId "<PORTAL_ID>"
```
`{ "status": "ok" }` means a report is available. `{ "status": "empty" }` means no completed scan exists.
---
## 3. Choose an action
Skip in **review mode** — go straight to Step 4 (which always runs in review mode).
MUST use plain language only. Never use words like CSP, CORS, OWASP, hardening, or scan profile.
### Default approach
<!-- gate: scan-site:3.action-choice | category=plan | cancel-leaves=nothing -->
> 🚦 **Gate (plan · scan-site:3.action-choice):** Recommend an action based on the site's scan state (running, idle, has report, no report), then ask the user to accept or choose differently. Starting a new scan triggers a multi-minute backend run; using an existing report is free.
>
> **Trigger:** Phase 3 entry (interactive mode only — review mode bypasses to step 4).
> **Why we ask:** Auto-starting a new scan wastes minutes if a recent report already answers the question; auto-using a stale report misses recent findings.
> **Cancel leaves:** Nothing — no scan triggered, no report consumed.
Analyze the site's current state and **recommend the single most relevant action** via `AskUserQuestion`:
- Scan running, no completed report → recommend waiting for the running scan to finish.
- Scan running, report exists → recommend showing the latest results while the new scan continues.
- Idle, no completed report → recommend running a new scan.
- Idle, recent report exists → ask whether to use the existing report or run a fresh scan.
If the site's state does not warrant a specific recommendation, do not force one — ask what the user wants to do.
### Option rules
<!-- not-a-gate: meta-documentation describing how to structure `AskUserQuestion` options in this skill — not a literal call site. The actual prompt ("use existing report / run a fresh scan") fires dynamically in §3 Default approach. See approval-gates.md §6.24a + §6.27. -->
When presenting options>-
>-
Adds Azure DevOps connector to a Power Apps code app. Use when querying work items, creating bugs, managing pipelines, or making ADO API calls.
Internal implementation skill invoked by /add-native for camera, image picker, barcode scanner, QR scanner, and camera/gallery Dataverse artifact workflows.
>-
Use when adding a Power Platform connector to an Expo/React Native Power Apps mobile app and no dedicated mobile connector skill exists.
Guide the user to add a data source, connection, or API connector to a Canvas App via Power Apps Studio, then verify and continue. USE WHEN the user asks to add a data source, add a connection, add an API, add a connector, connect to SharePoint / Dataverse / SQL / Excel / OneDrive / Teams / Office 365, or any similar request to make new data available to the app. DO NOT USE WHEN the user is asking to list or describe existing data sources — call list_data_sources or list_apis directly instead.
Use when adding an unspecified data source to an Expo/React Native Power Apps mobile app; routes to Dataverse, SharePoint, or another connector.