Files
isphere-ai-bridge/docs/superpowers/specs/2026-07-09-n15-selector-report-hardening-design.md
2026-07-09 17:04:19 +08:00

7.1 KiB
Raw Blame History

N15 Selector Report Hardening Design

日期2026-07-09

1. 背景

N14 已在 main 上合并并推送,提供了离线 UIA selector 报告能力:

  • internal/uiaselector/report.go报告模型、catalog report generator、JSON/Markdown renderer。
  • cmd/uia-selector-report/main.go:离线 CLI只接受 dump 文件路径和输出报告路径。
  • scripts/verify-n14-uia-selector-report.ps1:运行测试、生成报告并做基础安全扫描。

N14 最终 review 留下两个非阻塞建议:

  1. StrictExitAmbiguous 分支缺少显式单测。
  2. verifier 的输出安全检查仍以字符串扫描为主,并且运行 CLI 前没有清理旧输出文件。

N15 的目标是把这些 review 建议收口为一个小型加固节点,不扩展产品能力,不接触 live UI不新增 helper op不新增 MCP tool不扩展 live action surface。

2. 目标

N15 只做报告安全与验证加固:

  1. 增加 StrictExitAmbiguous 显式单测,锁定 ambiguous_count > 0 时严格模式返回 3
  2. 加固 scripts/verify-n14-uia-selector-report.ps1,避免旧报告掩盖 CLI 输出回归。
  3. 增加输出文件检查:必须存在、必须是文件、大小必须大于 0。
  4. 增加 JSON 字段 allowlist 检查,确认 selectors[].matches[] 只包含允许的结构字段。
  5. 保持 N14 报告格式和 CLI 参数不变。

3. 非目标

N15 不做以下事项:

新增 selector
修改 selector catalog
修改报告 JSON/Markdown 格式
新增 CLI 参数
新增 helper op
新增 MCP tool
连接内网 iSphere
live dump 当前窗口
使用 hwnd/process 参数
点击控件
输入文本
打开会话
搜索联系人
发送消息
发送文件
接收文件
读取聊天内容
导出完整 visible text

N15 也不清理本地分支。分支清理可以在 N15 合并后单独执行。

4. 推荐方案

采用“小范围加固”方案,只修改两个文件:

internal/uiaselector/report_test.go
scripts/verify-n14-uia-selector-report.ps1

不修改:

native/ISphereWinHelper/Program.cs
internal/tools/winhelper.go
cmd/isphere-mcp/main.go
cmd/uia-selector-report/main.go
internal/uiaselector/report.go

理由N14 的实现逻辑已经通过最终验证和 review。N15 只需要补测试和 verifier 防回归能力,不需要改变生产代码路径。

5. Ambiguous strict-mode 测试设计

internal/uiaselector/report_test.go 中新增测试:

TestStrictModeClassifiesAmbiguousMatches

测试构造一个最小 UIA tree

root Window frmMain
  child Pane duplicate
  child Pane duplicate

测试 catalog 只包含一个 read-only selector

id: duplicate_panel
allowed_use: read_only_locate
required:
  automation_id: duplicate
  control_type: Pane
  framework_id: WinForm

期望:

report.OK == true
report.CatalogSize == 1
report.MatchedCount == 1
report.UnmatchedCount == 0
report.AmbiguousCount == 1
report.Selectors[0].MatchCount == 2
StrictExitCode(report) == StrictExitAmbiguous

该测试只使用内存构造的 Node,不依赖 N12R fixture也不涉及 visible text。

6. Verifier 加固设计

6.1 清理旧输出

go run ./cmd/uia-selector-report 之前删除旧报告:

Remove-Item -LiteralPath $jsonPath, $mdPath -Force -ErrorAction SilentlyContinue

目的:如果未来 CLI 回归为“退出 0 但未写新文件”,旧报告不会让后续存在性检查误通过。

6.2 文件存在和非空检查

替换当前 Test-Path 检查为更严格的文件检查:

$jsonItem = Get-Item -LiteralPath $jsonPath -ErrorAction Stop
if (-not $jsonItem.PSIsContainer -eq $false) { ... }
if ($jsonItem.Length -le 0) { ... }

实际实现应避免 PowerShell 运算优先级歧义,推荐使用清晰表达:

if ($jsonItem.PSIsContainer) { throw "JSON report path is not a file: $jsonPath" }
if ($jsonItem.Length -le 0) { throw "JSON report is empty: $jsonPath" }

Markdown 报告同样检查。

6.3 JSON match 字段 allowlist

解析 JSON 后,对每个 selectors[].matches[] 对象检查字段集合,只允许:

path
control_type
automation_id
class_name
framework_id
has_name
name_length

禁止出现:

name
value
text
visible_text

实现策略:

  1. 遍历 $report.selectors
  2. 遍历 $selector.matches
  3. 对每个 match 的 $match.PSObject.Properties.Name 做集合比较。
  4. 遇到不在 allowlist 中的字段立即失败。
  5. 如果字段名是 namevalue,错误信息明确指出这是 visible text 字段泄漏。

这比单纯扫描字符串更稳,因为它检查的是 JSON 对象结构。

6.4 保留字符串扫描

保留 N14 已有字符串扫描,用于发现:

"name"
"value"
国网甘肃省电力公司
send_after_approval
send_file_after_approval
receive_file_after_approval
open_conversation
InvokePattern
ValuePattern.SetValue

字段 allowlist 和字符串扫描同时存在:

  • allowlist 负责结构化字段泄漏。
  • 字符串扫描负责已知敏感文本和动作词防线。

7. 测试与验证策略

N15 implementation plan 必须采用 TDD。

最小验证命令:

go test ./internal/uiaselector -run "TestStrictModeClassifiesAmbiguousMatches" -count=1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-n14-uia-selector-report.ps1
go test ./...

最终验证命令:

go test ./internal/uiaselector ./cmd/uia-selector-report -count=1
go test ./...
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-n14-uia-selector-report.ps1
git diff --name-only main..HEAD -- native/ISphereWinHelper/Program.cs internal/tools/winhelper.go cmd/isphere-mcp/main.go

最后一条命令期望无输出。

8. 风险与缓解

风险 1PowerShell 对单元素数组展开造成遍历差异

缓解:实现遍历时使用 @($report.selectors)@($selector.matches),确保单元素和多元素都按数组处理。

风险 2allowlist 检查误报 PowerShell 内置属性

缓解:只读取 $match.PSObject.Properties.Name,这返回 JSON 对象自身属性,不读取方法或扩展成员。

风险 3旧输出清理误删非目标文件

缓解:只删除两个明确计算出的文件路径:

runs/n14-selector-report/n12r-selector-report.json
runs/n14-selector-report/n12r-selector-report.md

不递归删除目录。

9. 验收条件

N15 可接受条件:

  1. StrictExitAmbiguous 有显式单测。
  2. verifier 运行 CLI 前删除旧 JSON/Markdown 输出文件。
  3. verifier 检查 JSON/Markdown 输出存在、是文件、非空。
  4. verifier 对 JSON matches[] 执行字段 allowlist 检查。
  5. verifier 继续扫描 "name""value"、已知 visible text 和动作词。
  6. go test ./... 通过。
  7. scripts\verify-n14-uia-selector-report.ps1 通过。
  8. 不修改 helper/MCP/live action 文件。

10. 推荐下一步

下一步写 N15 implementation plan拆分为两个小任务

  1. TDD 增加 ambiguous strict-mode 单测。
  2. 加固 verifier 的旧输出清理、文件检查和 JSON 字段 allowlist。