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

266 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 不做以下事项:
```text
新增 selector
修改 selector catalog
修改报告 JSON/Markdown 格式
新增 CLI 参数
新增 helper op
新增 MCP tool
连接内网 iSphere
live dump 当前窗口
使用 hwnd/process 参数
点击控件
输入文本
打开会话
搜索联系人
发送消息
发送文件
接收文件
读取聊天内容
导出完整 visible text
```
N15 也不清理本地分支。分支清理可以在 N15 合并后单独执行。
## 4. 推荐方案
采用“小范围加固”方案,只修改两个文件:
```text
internal/uiaselector/report_test.go
scripts/verify-n14-uia-selector-report.ps1
```
不修改:
```text
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` 中新增测试:
```text
TestStrictModeClassifiesAmbiguousMatches
```
测试构造一个最小 UIA tree
```text
root Window frmMain
child Pane duplicate
child Pane duplicate
```
测试 catalog 只包含一个 read-only selector
```text
id: duplicate_panel
allowed_use: read_only_locate
required:
automation_id: duplicate
control_type: Pane
framework_id: WinForm
```
期望:
```text
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` 之前删除旧报告:
```powershell
Remove-Item -LiteralPath $jsonPath, $mdPath -Force -ErrorAction SilentlyContinue
```
目的:如果未来 CLI 回归为“退出 0 但未写新文件”,旧报告不会让后续存在性检查误通过。
### 6.2 文件存在和非空检查
替换当前 `Test-Path` 检查为更严格的文件检查:
```powershell
$jsonItem = Get-Item -LiteralPath $jsonPath -ErrorAction Stop
if (-not $jsonItem.PSIsContainer -eq $false) { ... }
if ($jsonItem.Length -le 0) { ... }
```
实际实现应避免 PowerShell 运算优先级歧义,推荐使用清晰表达:
```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[]` 对象检查字段集合,只允许:
```text
path
control_type
automation_id
class_name
framework_id
has_name
name_length
```
禁止出现:
```text
name
value
text
visible_text
```
实现策略:
1. 遍历 `$report.selectors`
2. 遍历 `$selector.matches`
3. 对每个 match 的 `$match.PSObject.Properties.Name` 做集合比较。
4. 遇到不在 allowlist 中的字段立即失败。
5. 如果字段名是 `name``value`,错误信息明确指出这是 visible text 字段泄漏。
这比单纯扫描字符串更稳,因为它检查的是 JSON 对象结构。
### 6.4 保留字符串扫描
保留 N14 已有字符串扫描,用于发现:
```text
"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。
最小验证命令:
```powershell
go test ./internal/uiaselector -run "TestStrictModeClassifiesAmbiguousMatches" -count=1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-n14-uia-selector-report.ps1
go test ./...
```
最终验证命令:
```powershell
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旧输出清理误删非目标文件
缓解:只删除两个明确计算出的文件路径:
```text
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。