关于 scan-site
scan-site 是面向 Power Pages 项目的安全技能,可扫描已部署的网站、获取最新报告并概述发现。它检查线上站点,而非本地源代码。
主要功能
- 01扫描已部署的 Power Pages 网站
- 02检查扫描状态和最新报告
- 03用通俗语言概述发现
- 04生成 HTML 报告
- 05建议后续安全技能
适用场景
- 01查看网站当前的安全问题
- 02扫描已部署的网站
- 03以 HTML 报告分享扫描结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill scan-site安装后在 Claude Code 中输入/scan-site确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/commands.mdscan-reference.mdscripts/get-latest-report.jspoll-deep-scan.jsstart-deep-scan.jstransform-report.js详细介绍
插件检查:运行
node "${PLUGIN_ROOT}/scripts/check-version.js"— 如果输出了消息,请在继续之前向用户显示该消息。
扫描站点
对已部署的 Power Pages 站点运行安全扫描,获取最新扫描报告,并以通俗易懂的摘要呈现发现结果。扫描在服务器端运行;耗时取决于站点规模——小型站点几分钟即可完成,大型站点可能需要数小时。
此技能扫描的是实时部署的站点,而不是本地源代码。
初始请求: $ARGUMENTS
注意事项
- 网站记录 ID 与门户 ID。
.powerpages-site/website.yml存储的是网站记录 ID,而不是门户 ID。每个脚本都需要--portalId。在前置步骤中使用website.js --websiteId解析一次。 - 切勿按名称解析。 环境内的站点名称可能重复;只有网站记录 ID 是安全的。
- 解析器返回
null表示站点尚未部署,或已通过身份验证的配置文件指向了其他环境。 - 扫描耗时较长。 耗时取决于站点规模——小型站点几分钟即可完成,大型站点可能需要数小时。在后台轮询,大型站点请增大
--timeoutMinutes。 - 同一站点一次只能运行一个扫描。 如果有扫描正在运行时启动新扫描,会返回
Z003—start-deep-scan.js会将其报告为{ "status": "already-running" }(退出码为 0)。 - 可能会受到速率限制。 服务可能会限制对同一站点重复扫描的频率。遇到限流时,请等待后再重试。
- 尚无已完成的扫描。 新站点或正在扫描的站点没有已完成的报告 —
get-latest-report.js会返回{ "status": "empty" }。
工作流
- 前置步骤 — 定位项目、确认已登录、识别站点
- 检查扫描状态 — 检查是否有扫描正在运行
- 选择操作 — 提供结合上下文的建议(运行新扫描 / 显示最新结果)
- 运行扫描 — 启动扫描并轮询直至完成
- 获取并总结 — 获取报告并呈现发现结果
- 逐项处理后续事项 — 将问题转交给合适的下游技能(仅当报告中包含问题时)
任务跟踪
创建四组任务。每项任务开始时标记为 in_progress,完成后标记为 completed。
| 组 | 创建时机 | 任务 |
|---|---|---|
| 1 | 开始时 | 检查前置条件 |
| 2 | 前置条件通过后 | 检查扫描状态 · 选择操作(审核模式下跳过) |
| 3 | 用户确认操作后(或处于审核模式时) | 运行扫描(仅在交互模式下用户选择查看最新结果时跳过) · 获取并总结(始终执行) |
| 4 | 获取并总结后 | 逐项处理后续事项(仅当报告包含问题且不处于审核模式时) |
1. 前置步骤
1.1 定位项目并检测审核模式
使用 Glob 查找 **/powerpages.config.json。如果 $ARGUMENTS 包含 --review <out-dir>,请记住输出目录 — 跳过第 3 步(选择操作),自动执行第 4 步(运行扫描,启动新扫描或连接到正在运行的扫描),第 5 步仅写入 JSON,并跳过第 6 步(后续事项)。
1.2 解析站点标识符
读取 .powerpages-site/website.yml → 提取 id 字段 → 即为 <WEBSITE_ID>。
如果缺少该字段,则站点尚未部署。告知用户并建议运行 /deploy-site。停止。不要按名称或 URL 解析。
解析为 portalId:
node "${PLUGIN_ROOT}/scripts/website.js" --websiteId "<WEBSITE_ID>"
记录 Id(portalId)、Type、Name、WebsiteUrl。如果退出码为 2 → 需要登录(pac auth create 或 az login)。如果为 null → 在此环境中找不到站点。两种情况都应停止。
2. 检查扫描状态
node "${PLUGIN_ROOT}/skills/scan-site/scripts/poll-deep-scan.js" --portalId "<PORTAL_ID>" --once
--once 执行一次状态检查,退出码为 0,并输出:
{ "status": "ongoing" }→ 当前有扫描正在运行。{ "status": "idle" }→ 没有扫描正在运行。
然后调用 get-latest-report.js,确认是否存在已完成的报告:
node "${PLUGIN_ROOT}/skills/scan-site/scripts/get-latest-report.js" --portalId "<PORTAL_ID>"
{ "status": "ok" } 表示有可用报告。{ "status": "empty" } 表示尚无已完成的扫描。
3. 选择操作
在审查模式下跳过,直接进入第 4 步(该步骤始终会在审查模式下运行)。
必须只使用通俗易懂的语言。绝不要使用 CSP、CORS、OWASP、hardening 或 scan profile 等词。
默认方案
🚦 关卡(计划 · scan-site:3.action-choice): 根据网站的扫描状态(运行中、空闲、有报告、无报告)推荐一项操作,然后询问用户是接受还是另选操作。开始新扫描会触发持续数分钟的后端任务;使用现有报告不收费。
触发条件: 进入阶段 3(仅交互模式——审查模式会跳过此步骤,直接进入第 4 步)。 询问原因: 如果近期报告已经能回答问题,自动开始新扫描会浪费数分钟;如果自动使用过时报告,则会遗漏近期发现。 取消后: 无任何变化——不会触发扫描,也不会使用报告。
分析网站的当前状态,并通过 AskUserQuestion 推荐最相关的一项操作:
- 扫描正在运行,且没有已完成的报告 → 建议等待当前扫描完成。
- 扫描正在运行,且已有报告 → 建议在新扫描继续运行时查看最新结果。
- 空闲,且没有已完成的报告 → 建议运行新扫描。
- 空闲,且有近期报告 → 询问用户是使用现有报告,还是运行新扫描。
如果网站状态不足以支持具体建议,就不要强行推荐;询问用户想要进行什么操作。
选项规则
通过 AskUserQuestion 提供选项时:
label保持在 1–5 个词。每个选项都要包含description。- 对于会触发新扫描的选项,请在该选项的
description中说明相关注意事项,确保用户在决定时了解这些信息。用户选择该选项后,不要再单独询问确认。 - 仅当选项代表具体变更(开始新扫描)时才包含
preview。不要为“显示最新结果”或信息类选项添加preview。 - 只显示当前状态下可执行的选项。如果扫描已在运行,不要提供“开始新扫描”。
- 仅当网站状态支持时才标记“(推荐)”。
4. 运行扫描
在审查模式下,始终执行此步骤:如果扫描已在运行,则接入该扫描并轮询;否则开始新扫描并轮询。不要询问——审查模式会在无需用户交互的情况下端到端运行。
在交互模式下,如果用户选择查看最新结果,则跳过此步骤。
开始扫描:
node "${PLUGIN_ROOT}/skills/scan-site/scripts/start-deep-scan.js" --portalId "<PORTAL_ID>"
如果标准输出为 { "status": "already-running" },则跳转到轮询步骤——已有扫描正在进行。
然后轮询,直至扫描完成:
node "${PLUGIN_ROOT}/skills/scan-site/scripts/poll-deep-scan.js" --portalId "<PORTAL_ID>"
使用 run_in_background: true 运行轮询,以便用户继续工作。脚本会在扫描完成或超时后退出(默认 20 分钟)。如果超时,则获取当前可用的报告,并在摘要中注明超时。
5. 获取并总结
5.1 获取并转换报告
node "${PLUGIN_ROOT}/skills/scan-site/scripts/transform-report.js" --portalId "<PORTAL_ID>"
解析标准输出中的 JSON。状态字段可能为:
ok— 正常报告,包含findings和details。empty— 此网站没有已完成的扫描(例如,网站刚创建或扫描仍在运行)。记录一条说明此情况的info发现,然后继续。malformed— API 返回的响应缺少Rules数组。转换过程会生成一条描述此情况的warning发现;将其呈现给用户,并建议重新运行扫描。
有关脚本应用的 Risk → 严重性映射,请参阅 references/scan-reference.md。
5.2 审查模式
在审查模式下,跳过 HTML 报告,并将转换后的标准输出写入 <REVIEW_DIR>/scan-site.json。然后停止。转换过程会生成 { status, findings, details };编排技能负责呈现结果。
5.3 渲染 HTML 报告
在审核模式下跳过。
渲染时使用与汇总安全审核相同的共享模板。构建包含单个部分的审核数据负载,然后进行渲染:
node "${PLUGIN_ROOT}/scripts/build-review-data.js" \
--reportName "Site Scan" \
--inputDir "<TEMP_DIR>" \
--siteName "<SITE_NAME>" \
--goalLabel "Live Site Scan" \
--scopeLabel "<SCOPE_LABEL>" \
--summary "<SUMMARY_TEXT>" \
--output "<TEMP_DIR>/data.json"
node "${PLUGIN_ROOT}/scripts/render-review.js" \
--data "<TEMP_DIR>/data.json" \
--output "<PROJECT_ROOT>/docs/site-scan-<YYYY-MM-DD-HHMMSS>.html"
<TEMP_DIR> 中只能包含 scan-site.json(第 5.1 步的转换输出)——build-review-data.js 会忽略中间文件。文件名必须包含本地时间戳(例如,site-scan-2026-05-14-053805.html)。渲染成功后删除 <TEMP_DIR>。在浏览器中打开渲染后的 HTML。
5.4 提供摘要
在聊天中用通俗易懂的语言总结:发现总数、按严重程度分类的数量,以及可能的自上次扫描以来的变化。不要一开始就提技术名称。
5.5 记录技能使用情况
参考:
${PLUGIN_ROOT}/references/skill-tracking-reference.md使用
--skillName "ScanSite"。
6. 引导后续操作
在审核模式下跳过。如果报告中没有问题,也跳过。
按可提供帮助的下游技能对发现进行分组:
- 标头 / Cookie 问题 →
/manage-headers - WAF / 防火墙问题(阻止机器人、限制页面访问频率、限制 IP / 国家或地区)→
/manage-firewall - 权限问题 →
/audit-permissions检查现有表权限,和/或/create-webroles设置基于角色的访问权限 - 登录或外部身份问题 →
/setup-auth - 代码层面的问题(暴露的调试页面、信息泄露、源代码公开可见)→ 建议手动修复代码;没有可用于处理这些发现的路由技能
只建议与报告中实际存在的发现相匹配的技能。如果某项发现不对应任何技能,应将其作为用户可采取行动的手动后续事项提出。如果没有有意义的后续操作,则结束该技能——不要为了提问而提问。
约束
- 通俗易懂——绝不能对用户使用技术术语。使用日常用语;只有在用户询问时才解释技术名称。
- 只读——此技能只运行扫描并读取结果。它不会启用 WAF、删除扫描或更改网站配置。
- 后台长时间运行的调用——启动扫描后,通过
run_in_background: true轮询,以便用户继续工作。 - 结合上下文进行交互——每项建议都必须反映网站的当前状态:
- 当扫描正在运行时,绝不要提供“开始新扫描”。
- 没有已完成的报告时,绝不要提供“显示最新结果”。
- 只有当前状态支持时,才标注“(推荐)”。
- 预览仅用于审查更改——仅在会启动新扫描的选项中包含
preview。不要将其添加到导航或信息类选项中。
参考资料
references/commands.md——脚本标志、响应结构、错误目录和操作说明。脚本返回非零退出代码时,请阅读“常见错误目录”一节。references/scan-reference.md——深度扫描报告的字段级架构、警报风险值、规则状态和严重程度映射。规范化发现时请阅读。