7.1 KiB
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 留下两个非阻塞建议:
StrictExitAmbiguous分支缺少显式单测。- verifier 的输出安全检查仍以字符串扫描为主,并且运行 CLI 前没有清理旧输出文件。
N15 的目标是把这些 review 建议收口为一个小型加固节点,不扩展产品能力,不接触 live UI,不新增 helper op,不新增 MCP tool,不扩展 live action surface。
2. 目标
N15 只做报告安全与验证加固:
- 增加
StrictExitAmbiguous显式单测,锁定ambiguous_count > 0时严格模式返回3。 - 加固
scripts/verify-n14-uia-selector-report.ps1,避免旧报告掩盖 CLI 输出回归。 - 增加输出文件检查:必须存在、必须是文件、大小必须大于 0。
- 增加 JSON 字段 allowlist 检查,确认
selectors[].matches[]只包含允许的结构字段。 - 保持 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
实现策略:
- 遍历
$report.selectors。 - 遍历
$selector.matches。 - 对每个 match 的
$match.PSObject.Properties.Name做集合比较。 - 遇到不在 allowlist 中的字段立即失败。
- 如果字段名是
name或value,错误信息明确指出这是 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. 风险与缓解
风险 1:PowerShell 对单元素数组展开造成遍历差异
缓解:实现遍历时使用 @($report.selectors) 和 @($selector.matches),确保单元素和多元素都按数组处理。
风险 2:allowlist 检查误报 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 可接受条件:
StrictExitAmbiguous有显式单测。- verifier 运行 CLI 前删除旧 JSON/Markdown 输出文件。
- verifier 检查 JSON/Markdown 输出存在、是文件、非空。
- verifier 对 JSON
matches[]执行字段 allowlist 检查。 - verifier 继续扫描
"name"、"value"、已知 visible text 和动作词。 go test ./...通过。scripts\verify-n14-uia-selector-report.ps1通过。- 不修改 helper/MCP/live action 文件。
10. 推荐下一步
下一步写 N15 implementation plan,拆分为两个小任务:
- TDD 增加 ambiguous strict-mode 单测。
- 加固 verifier 的旧输出清理、文件检查和 JSON 字段 allowlist。