Implement unsafe icon cache diagnostics (T-611)
This commit is contained in:
@@ -21,6 +21,8 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
7. [`tasks/README.md`](tasks/README.md):任务文件约定(一任务一文件);本轮任务从 `docs/tasks/` 领取。
|
||||
8. [`current-state.md`](current-state.md):当前代码现实、可运行命令、下一步任务。
|
||||
|
||||
遇到 `unsafe_cache` 或需要人工处理图标缓存时,只使用 [`troubleshooting.md`](troubleshooting.md) 的不跟随链接 runbook;不要在任务中临时发明递归清理命令。
|
||||
|
||||
日常会话不需要机械重读全部文档:
|
||||
|
||||
1. 读取仓库级规则(`AGENTS.md`)和 [`agent-context.json`](agent-context.json)。
|
||||
@@ -45,7 +47,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
|
||||
## 当前阶段
|
||||
|
||||
当前项目已完成 Phase 0~2、T-301 与审核整改 `T-604`~`T-610`。Windows 安全路径阻断项、图标缓存资源边界、后台结果回 UI 线程的事件接线、双适配器交互契约、`VisibleItems` 快照生命周期和双端 Gio shell 职责拆分均已关闭;`T-611` 已落成,下一步执行 unsafe cache 真实错误贯通、双端安全提示与人工恢复指引,其余 Phase 1 整改继续串行处理,T-302 暂后置。
|
||||
当前项目已完成 Phase 0~2、T-301 与审核整改 `T-604`~`T-611`。Windows 安全路径阻断项、图标缓存资源边界、后台结果回 UI 线程的事件接线、双适配器交互契约、`VisibleItems` 快照生命周期、双端 Gio shell 职责拆分以及 unsafe cache 安全诊断/runbook 均已关闭;下一步按 Phase 1 安全审核最终顺序落成中央目录/EOCD 预扫描边界任务,T-302 暂后置。
|
||||
|
||||
优先路径:
|
||||
|
||||
@@ -53,7 +55,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
2. 已完成 Phase 1:清单验签、ZIP 安全解压、原子切换回滚原型。
|
||||
3. 已完成 Phase 2 与 T-301:清单/列表/详情/图标缓存 + 可恢复下载队列。
|
||||
4. 已完成 T-604:modern/Win7 workspace 与 Gio 版本解析彻底隔离。
|
||||
5. 已完成 T-606~T-610:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期和双端 `shell.go` 同 package 镜像职责拆分;下一步执行已落成的 T-611 unsafe cache 诊断/人工恢复指引,再串行处理其余整改与 T-302/T-303、Phase 4-6。
|
||||
5. 已完成 T-606~T-611:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期、双端 `shell.go` 同 package 镜像职责拆分和 unsafe cache 诊断/人工恢复指引;下一步串行处理 Phase 1 中央目录预扫描整改,再继续 T-302/T-303 与 Phase 4-6。
|
||||
|
||||
## 领取任务规则
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ T-610 在两个隔离 `ui/gio` package 内采用相同文件职责:`shell.go`
|
||||
|
||||
T-204/T-606/T-607 图标链路为 `Catalog icon digest + DPI → 32 MiB/256-key memory LRU → verified disk → 流式 IconFetcher(maxBytes+1) → SHA-256/图片资源限制校验 → 原子磁盘缓存 → 后台 DecodeIcon → IconReady/IconFailed application event → bounded FIFO relay + Window.Invalidate → Frame/UI ApplyEvent → ApplyIcon(paint.ImageOp)`。同一 key 由一个 in-flight leader 去重,不同 key 的磁盘/网络工作并行;全局锁只保护 memory/LRU/in-flight 元数据。relay 队列满时无损背压且可由 context/close 取消,后台从不修改 shell map。UI 只接受当前 app 最新且 icon_ref/DPI 匹配的 request_id;删除 app、替换 IconRef 或取消会使迟到结果失效,替换 IconRef 同时清除旧 ImageOp。磁盘与远端都重新校验,断网只使用已验证磁盘缓存;详情右栏只读取 `CatalogListModel.SelectedItem` 与内存 ImageOp,关闭详情不清空筛选或列表位置。
|
||||
|
||||
T-611 固定该链路的 fail-closed 诊断:磁盘 entry 为 symlink/非普通文件时 `IconCache` 不读取、不删除且不 fetch,`IconEventDelivery` 只发布安全分类 `unsafe_cache`,同时把包含 `ErrIconCacheUnsafe` 的错误留给后台调用方。双端 shell 在 UI owner goroutine 保存完整失败 identity + code,只对当前选中软件显示 code/app ID/`<digest>-<dpi>.icon` locator 与人工恢复提示;生命周期与最新请求、reference/DPI、ready、取消和 app 删除绑定。正式安装目标 root 为 `%LOCALAPPDATA%\OwnSoftBox\cache\icons\`,但当前生产 `cmd` 尚未装配 IconCache/Delivery/Fetcher,constructor root 才是代码事实;人工步骤见 [故障排查](troubleshooting.md)。
|
||||
|
||||
## 三、仓库目录结构
|
||||
|
||||
```text
|
||||
@@ -125,7 +127,8 @@ soft_quay/
|
||||
├─ apps/<id>/ # current/ staging/ backup/ installed-app.json
|
||||
├─ data/<id>/ # 子软件用户数据(更新永不覆盖)
|
||||
├─ licenses/ # 许可证(更新永不覆盖)
|
||||
├─ cache/ # 清单缓存、图标缓存
|
||||
├─ cache/ # 清单缓存
|
||||
│ └─ icons/ # 正式装配的目标图标缓存根;当前以 NewIconCache(root, ...) 实参为事实
|
||||
├─ downloads/ # 下载临时文件 + 任务元数据
|
||||
├─ staging/ # 盒子自更新暂存
|
||||
├─ backups/ # 盒子自更新备份
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
- Gio Layout 每帧禁止 IO(磁盘/网络/哈希/图片解码);后台任务只发布 application.Event,不得直接调用 `ApplyIcon` 或改控件/map。后台 event pump 只入有界 relay 并调用 `Window.Invalidate`;只有 Frame/UI goroutine可以 drain `ApplyEvent`。relay 满队列不得静默丢事件,关闭/取消必须解除背压等待。
|
||||
- `CatalogListModel.VisibleItems()` 返回当前只读 snapshot generation:refilter 只在状态变化时构造新 backing array 后发布,同 generation 的每帧读取不得复制;调用方不得修改 slice/item/Tags。旧 generation 在后续 model 变化后保持稳定,但 model 仍是单 owner、非并发安全对象。
|
||||
- 图标 Fetcher 必须返回与 context 绑定的流,由 `IconCache` 在分配完整响应前执行声明长度拒绝与 `maxBytes+1` 有界读取;不得恢复为先读任意大 `[]byte` 再校验。缓存并发只允许按 key 去重,不得用横跨磁盘/网络的全局锁换取去重。
|
||||
- 图标缓存 entry 是 symlink/reparse point 或非普通文件时必须 fail closed:不读取/跟随、不自动 delete/rename/quarantine、不回退 Fetcher。上层只持有已验证的 icon event identity + 稳定 failure code;UI locator 只能由规范 digest + DPI 生成,不得加入绝对 root、raw error、URL/query、token 或 link target。人工处置只引用 [故障排查](troubleshooting.md)。
|
||||
|
||||
## 3. 安全纪律(违反即安全事故)
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ Phase 1 安全整改按 `docs/review/phase1-security-review.md` 的交叉复核
|
||||
|
||||
#### Phase 2 交叉审核加固
|
||||
|
||||
Phase 2 整改按 `docs/review/phase2-review.md` 的交叉复核定稿顺序串行落成。T-606 已关闭正式图标接入前的并发、读取和内存边界;T-607 建立后台图标结果经 application event 回到 Gio UI goroutine 的线程契约;T-608 为两个隔离 Gio 适配器建立交互契约;T-609 修正 `VisibleItems` 快照生命周期;T-610 在不改变行为的前提下拆分双端 Gio shell 职责;T-611 为不安全缓存补可定位诊断与人工恢复指引。
|
||||
Phase 2 整改按 `docs/review/phase2-review.md` 的交叉复核定稿顺序串行落成。T-606 已关闭正式图标接入前的并发、读取和内存边界;T-607 建立后台图标结果经 application event 回到 Gio UI goroutine 的线程契约;T-608 为两个隔离 Gio 适配器建立交互契约;T-609 修正 `VisibleItems` 快照生命周期;T-610 在不改变行为的前提下拆分双端 Gio shell 职责;T-611 已用真实 fail-closed 传播、双端安全诊断和人工 runbook 关闭最终观察项。
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -22,6 +22,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
- [已有项目接入清单](adoption-checklist.md):把本模板补进已有代码库时的迁移步骤(本项目从零开始,备查)。
|
||||
- [协议合约](api.md):Catalog 清单、标准软件包协议 v1、许可证、事件合约、CLI 参数。
|
||||
- [页面与视图结构](routes.md):Gio 视图划分、组件归属、交互硬约束。
|
||||
- [故障排查](troubleshooting.md):`unsafe_cache` 等人工诊断与不跟随链接的安全恢复步骤。
|
||||
- [当前实现状态](current-state.md):可覆盖的当前快照,记录仓库现实状态、可运行命令和下一步可做任务。
|
||||
- [Agent 上下文清单](agent-context.md) / [`agent-context.json`](agent-context.json) / [`Schema`](agent-context.schema.json):按任务类型选择文档、用提交 / 文件 SHA 避免重复读取。
|
||||
- [Gitea MCP 接入](gitea-mcp.md):可选的共享文档、Issue / PR 协调、安全配置和断连降级规则(当前未启用)。
|
||||
|
||||
+5
-1
@@ -93,6 +93,10 @@ Catalog `icon` v1 是 `sha256:<64 hex>` 内容引用,不是可直接请求的 UR
|
||||
8. `IconEventDelivery` 在调用方拥有的后台 context 中完成 `IconCache.Load` 与 `DecodeIcon`,成功发布 `IconReady`,失败只发布稳定分类的 `IconFailed`;取消直接结束且不发布迟到失败。事件身份为 request_id + app_id + icon_ref + DPI,不得把原始 URL/query 或 Gio 类型放进 payload。
|
||||
9. application event relay 是有界 FIFO,队列满时执行可取消的 lossless backpressure,不静默丢图标结果。后台 pump 成功入队后只调用并发安全的 `Window.Invalidate`;Gio Frame/UI goroutine 在 Layout 前 drain 并执行 `ApplyEvent`/`ApplyIcon`。
|
||||
10. shell 只接受当前 app 最新且 icon_ref/DPI 匹配的 request_id;删除 app、替换 IconRef、DPI 变化或取消请求后丢弃迟到 ready/failed。同一 AppID 更换 IconRef 时先清除旧 ImageOp,Layout 始终只复用内存 `paint.ImageOp`。
|
||||
11. symlink、目录和其他非普通 cache entry 必须返回 `ErrIconCacheUnsafe` 并立即停止;不得读取/跟随、删除、改写或重命名该 entry,不得回退 Fetcher。该错误由 delivery 映射成唯一稳定码 `unsafe_cache`,后台返回链仍保留 `ErrIconCacheUnsafe` 供调用方分类。
|
||||
12. shell 只为当前最新身份保存 `IconEventIdentity + IconFailureCode`;仅 `unsafe_cache` 在选中详情显示安全警告、稳定 code、app ID 与 `<digest>-<dpi>.icon` locator。不得显示绝对 cache root、原始 error、URL/query、token 或 link target;人工处理遵循 [故障排查](troubleshooting.md),不提供自动清理/隔离/重试操作。
|
||||
|
||||
当前生产 `cmd` 尚未装配 `NewIconCache`、真实 `IconFetcher` 或 `IconEventDelivery.LoadAndPublish` 调用方。以上是已由 core/双端适配器测试冻结的安全契约,不是生产网络图标链已经启用的声明。
|
||||
|
||||
## 2. 标准软件包协议 v1(ZIP)
|
||||
|
||||
@@ -284,7 +288,7 @@ phase 只允许:`prepared`、`current_backed_up`、`staging_activated`、`rollba
|
||||
| IconReady | 图标已完成可信加载与后台解码 | request_id, app_id, icon_ref, DPI, image.Image | 有界 relay 唤醒窗口;UI Frame 验证仍为最新请求后创建 ImageOp |
|
||||
| IconFailed | 图标加载、校验或解码失败(取消不发布) | request_id, app_id, icon_ref, DPI, error_code | UI 记录诊断;仅保留同 icon_ref/DPI 的既有可信图标,否则继续占位 |
|
||||
|
||||
错误码为稳定英文枚举(如 `hash_mismatch`, `zip_path_escape`, `disk_full`, `app_running`, `signature_invalid`),UI 负责本地化文案。图标事件只使用 `unavailable`、`invalid_content`、`unsafe_cache`,原始网络错误只返回后台调用方/日志,不得进入 UI payload。
|
||||
错误码为稳定英文枚举(如 `hash_mismatch`, `zip_path_escape`, `disk_full`, `app_running`, `signature_invalid`),UI 负责本地化文案。图标事件只使用 `unavailable`、`invalid_content`、`unsafe_cache`,原始网络错误只返回后台调用方/日志,不得进入 UI payload。`unsafe_cache` 详情只使用 event 中已经验证的 app ID、icon_ref 与 DPI 生成 `<digest>-<dpi>.icon` locator;不得把 cache root、原始错误或 link target 补进 event/UI。
|
||||
|
||||
## 5. CLI 参数合约
|
||||
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-18
|
||||
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列已完成;审核整改 T-604~T-610 已完成,T-611 已落成待执行,T-302 继续暂后置
|
||||
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列已完成;审核整改 T-604~T-611 已完成,T-302 继续暂后置
|
||||
- 技术栈:根 Go 1.25 workspace 只纳入 core/app-modern,`app-win7/go.work` 独立纳入 core/app-win7;版本闸门证明 modern Gio v0.10.1 与 win7 Gio v0.6.0 不交叉解析
|
||||
- 生产代码:core 已有 Catalog/本地状态/存储、共享 Windows 安全相对路径策略、安全 ZIP 解压/回滚原型、发布稳定只读 generation 的无 IO 软件列表模型、按 key in-flight + 流式有界读取 + 32 MiB/256-key LRU 的可信图标缓存、图标 Load/Decode 事件发布用例、有界 application event relay,以及默认并发 2 的持久可恢复下载队列;modern/win7 主循环已接 relay/Invalidate,AppShell 已实现搜索/分类/视图、惰性列表、详情右栏、图标请求身份与 UI-only ApplyEvent/ApplyIcon,并按 root/header/catalog/detail/style 同 package 镜像职责拆文件
|
||||
- 测试:core 覆盖 Catalog、列表快照 generation/零复制、SemVer/12 状态、本地安装记录、Windows dot-space/设备名/Unicode 折叠路径攻击、ZIP destination 包含性、图标并发/取消/读取边界/LRU、图标事件身份/失败分类/relay 背压与关闭、下载并发/暂停/取消/重试/Range/断连/恢复/事件失败与文件身份替换;两个 app 覆盖 Editor/视图/分类/行/恢复/关闭接线、500 项 viewport、AppID 控件与分类控件生命周期、详情上下文、空状态语义、UI drain 前后、最新/取消/换引用图标结果与平台 stub;安装恢复矩阵保持通过
|
||||
- 生产代码:core 已有 Catalog/本地状态/存储、共享 Windows 安全相对路径策略、安全 ZIP 解压/回滚原型、发布稳定只读 generation 的无 IO 软件列表模型、按 key in-flight + 流式有界读取 + 32 MiB/256-key LRU 的可信图标缓存、图标 Load/Decode 事件发布用例、有界 application event relay,以及默认并发 2 的持久可恢复下载队列;modern/win7 主循环已接 relay/Invalidate,AppShell 已实现搜索/分类/视图、惰性列表、详情右栏、完整图标失败 identity 生命周期与仅 `unsafe_cache` 可见的安全 locator/人工恢复提示,并按 root/header/catalog/detail/style 同 package 镜像职责拆文件
|
||||
- 测试:core 覆盖 Catalog、列表快照 generation/零复制、SemVer/12 状态、本地安装记录、Windows dot-space/设备名/Unicode 折叠路径攻击、ZIP destination 包含性、图标并发/取消/读取边界/LRU、真实目录/symlink fail-closed 与 cache→`unsafe_cache` event、relay 背压与关闭、下载并发/暂停/取消/重试/Range/断连/恢复/事件失败与文件身份替换;两个 app 覆盖 Editor/视图/分类/行/恢复/关闭接线、500 项 viewport、AppID 控件与分类控件生命周期、详情上下文、空状态语义、UI drain 前后、图标失败身份生命周期与 `unsafe_cache` 详情语义;安装恢复矩阵保持通过
|
||||
- 数据:`schemas/` 已有 manifest/app.json/installed-app.json/download-task.json v1 Schema并注明 Windows 路径运行时权威规则;`testdata/catalog/` 有公开虚构清单样例;`testdata/zip/` 与 `testdata/download/` 记录运行时生成的攻击/传输矩阵
|
||||
- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、执行完整 Phase 0 闸门、打印双目标构建命令)
|
||||
- 标准验证路径:`bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`
|
||||
- 版本管理:git 已初始化,main 分支,远端 origin 为 Gitea `opc/soft_quay`;harness 文档已提交
|
||||
- 当前 blocker:无;下一步领取并执行 `T-611`,用真实 cache→event 测试、双端可见提示和人工 runbook 闭合 unsafe cache 诊断;自动 quarantine 另做威胁模型,Phase 1 后续整改与 T-302 继续后置
|
||||
- 当前 blocker:无;T-611 已闭合 unsafe cache 诊断且自动 quarantine 仍需独立威胁模型。下一步按 `docs/review/phase1-security-review.md` 最终处理顺序第 2 项落成后续任务(预计 T-612):在 `zip.OpenReader` 前增加包大小与中央目录/EOCD 预扫描边界;T-302 继续后置到该阻断项关闭
|
||||
|
||||
## 当前目录要点
|
||||
|
||||
| 路径 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301 与 T-604~T-610 已完成;T-611 已落成待执行,其余审核整改尚未编号,T-302 暂后置 |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301 与 T-604~T-611 已完成;Phase 1 后续审核整改尚未编号,T-302 暂后置 |
|
||||
| `scripts/` | 已有 | harness 治理、core 边界、Go 版本检查与 Phase 0 双平台验证入口 |
|
||||
| `core/` | 已建 | Go 1.20 兼容;已有正式 Catalog、本地状态/存储、共享 Windows safepath、列表模型、有界并发图标缓存、图标事件/relay、可恢复下载队列与 Phase 1 安装安全原型 |
|
||||
| `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1;Modern AppShell 已接入虚拟列表、详情、图标事件 drain/过期拒绝和内存 ImageOp,并拆为五类 shell 职责文件 |
|
||||
@@ -40,9 +40,9 @@
|
||||
|
||||
任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要:
|
||||
|
||||
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`;Phase 3 的 `T-301`;审核整改 `T-604`~`T-610`。
|
||||
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`;Phase 3 的 `T-301`;审核整改 `T-604`~`T-611`。
|
||||
- 正在进行:无。
|
||||
- 下一个可领取任务:`T-611`(依赖 `T-610` 已完成),不安全图标缓存诊断与人工恢复指引。
|
||||
- 下一个可领取任务:暂无已落成 TODO;下一步按 Phase 1 最终处理顺序第 2 项落成中央目录/EOCD 预扫描边界任务(预计 `T-612`)。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
|
||||
@@ -288,4 +288,4 @@ modern/win7 的 `ApplyIcon` 都直接写 `shell.icons` map,Layout 同时读取
|
||||
- `T-608` 已完成最终处理顺序第 3 项:modern/win7 使用除 edition/窗口尺寸外一致的场景矩阵,验证 Editor/Clickable 经 Layout 更新共享 model、重排后 AppID 行身份、详情关闭上下文、500 项 viewport、app/category controls 释放及两类空状态语义;双端定向重复测试与完整闸门通过,生产 `shell.go` 无需修正。
|
||||
- `T-609` 已完成最终处理顺序第 4 项:`refilter` 在局部新 backing array 完整构造后发布,旧 generation 跨五类公开 model mutation 保持稳定;同 generation 读取共享 backing 且零分配。core 定向重复测试、双 Gio 回归与完整闸门通过,未扩展为并发安全或防御性深拷贝。
|
||||
- `T-610` 已完成最终处理顺序第 5 项:双端 `shell.go` 只保留 AppShell 状态/生命周期和根编排,其余声明进入 header/navigation、catalog/list、detail、style 同 package 镜像职责文件;原 54 个声明与根调用链保持不变,双端适配器/图标回归及完整闸门通过。
|
||||
- `T-611` 已按最终处理顺序第 6 项落成待执行:用真实非普通 cache entry 贯通 `ErrIconCacheUnsafe → IconFailed/unsafe_cache`,双端只显示稳定安全字段与人工恢复指引;自动 quarantine 仍需独立威胁模型。
|
||||
- `T-611` 已完成最终处理顺序第 6 项:真实目录与 Windows symlink cache entry 均证明 fail closed、零 fetch 且不改写 entry/target;真实 cache→delivery 唯一发布 `IconFailed/unsafe_cache` 并保留后台 sentinel。双端保存完整失败 identity,只在当前详情显示安全 code/app/locator 与人工 runbook,生命周期/语义树、定向重复测试及完整闸门通过;自动 quarantine 仍需独立威胁模型。
|
||||
|
||||
+5
-3
@@ -12,7 +12,7 @@
|
||||
│ 视图 │ 每项:图标·名称·版本·状态·进度·主操作按钮 │
|
||||
│ 全部 │ │
|
||||
│ 已安装 │ 右侧/弹层:软件详情 │
|
||||
│ 可更新 │ 版本 · 简介 · 教程 · 授权状态 · 操作 │
|
||||
│ 可更新 │ 版本 · 简介 · 安全诊断 · 状态 · 操作 │
|
||||
│ 最近使用│ │
|
||||
├────────┴────────────────────────────────────┤
|
||||
│ 底部状态栏:网络 · 任务数 · 磁盘 · 盒子版本(Legacy 标识) │
|
||||
@@ -24,7 +24,7 @@
|
||||
| 视图 | 职责 | MVP |
|
||||
| --- | --- | --- |
|
||||
| 软件列表(主视图) | T-203 已实现全部/已安装/可更新切换、名称/ID/tag 即时搜索、单分类筛选、稳定滚动与明确空状态;下载中/最近使用和多标签复选随对应用例后续接入 | P0 |
|
||||
| 软件详情(弹层或右栏) | T-204 已实现右栏、关闭、版本/分类/简介/tags/状态/不可用原因/教程/主页展示与内存图标;真实操作和授权事实随对应模块接入 | P0 |
|
||||
| 软件详情(弹层或右栏) | T-204 已实现右栏、关闭、版本/分类/简介/tags/状态/不可用原因/教程/主页展示与内存图标;T-611 仅对当前 `unsafe_cache` 显示稳定安全诊断和人工恢复提示;真实操作和授权事实随对应模块接入 | P0 |
|
||||
| 下载队列 | 所有任务的进度、速度、剩余时间;暂停/取消/重试 | P0 |
|
||||
| 设置 | 并发数、目录、代理、自动检查更新、beta 通道、日志级别、便携模式(V2) | P0(最小集) |
|
||||
| 授权 | 许可证导入、已授权软件列表、machine 信息、换绑/申诉入口 | P0 |
|
||||
@@ -50,7 +50,7 @@ T-610 固定 modern 与 Win7 两个 `ui/gio` 适配器的同 package 文件职
|
||||
| `shell.go` | `AppShell` 状态/构造、`SetItems`/`ApplyIcon` 生命周期、根 `Layout` 与输入 drain |
|
||||
| `shell_header.go` | 顶部栏、分类、view/filter 导航与版本特有 footer |
|
||||
| `shell_catalog.go` | content/catalog、惰性列表、app row/icon 与空状态 |
|
||||
| `shell_detail.go` | 详情布局、详情字段与动作/回退文案 helper |
|
||||
| `shell_detail.go` | 详情布局、详情字段、`unsafe_cache` 安全诊断与动作/回退文案 helper |
|
||||
| `shell_style.go` | palette/theme、panel 绘制、view/status 文案与颜色 helper |
|
||||
|
||||
这些文件仍共同实现一个 `gio` package,不是新增页面 API 或状态层。modern-only 的 `layoutCatalog`/`layoutFooter`/`actionLabel` 等保留在对应职责文件,Win7 不增加空壳;后续视图应进入相应职责文件,不能重新把布局链堆回根 `shell.go`。
|
||||
@@ -83,6 +83,8 @@ T-204 已落地的详情/图标约束:
|
||||
- modern 与 Legacy 均显示版本、分类、简介、tags、状态、不可用原因、教程和主页文本;尚未接入的安装/启动/授权不伪装为已可执行操作。
|
||||
- T-607 由 `IconEventDelivery` 在后台完成可信加载/解码并发布强类型 `IconReady`/`IconFailed`;有界 FIFO relay 只请求 Invalidate,Frame/UI goroutine drain 后才调用 `ApplyEvent`/`ApplyIcon`。相同 app 只接受最新且 IconRef/DPI 匹配的 request_id;Catalog 删除、IconRef 变化或取消后丢弃迟到结果,IconRef 变化同时清除旧 ImageOp。
|
||||
- 图标未命中或离线缓存不可用时显示非 emoji 的字母占位,不阻塞列表或详情。
|
||||
- T-611 将最近一次已验证失败保存为完整 `IconEventIdentity + IconFailureCode`;新请求、匹配 ready、IconRef/DPI 变化、取消和 app 删除清除旧诊断,同引用 Catalog snapshot 可保留。只有 `unsafe_cache` 在当前选中详情显示文字告警、`unsafe_cache`、app ID 与 `<digest>-<dpi>.icon` locator;`unavailable`/`invalid_content` 保持普通占位,不冒充安全事件。
|
||||
- 安全告警必须有可访问标题、明确说明本次未继续远端获取/自动修复并指向 [故障排查](troubleshooting.md);颜色不能作为唯一信号,不得增加清理、隔离或重试按钮,不得显示绝对路径、原始 error、URL/query 或 link target。
|
||||
|
||||
## 导航规则
|
||||
|
||||
|
||||
+10
-3
@@ -3,13 +3,15 @@ id: T-611
|
||||
title: 建立不安全图标缓存诊断与人工恢复指引
|
||||
phase: 2
|
||||
deps: [T-610]
|
||||
status: TODO
|
||||
status: DONE
|
||||
created: 2026-07-18
|
||||
issue: null
|
||||
context_ref: null
|
||||
context_ref: 6455fec8115497ba73fc092366193ae67269b1bb
|
||||
claim_branch: null
|
||||
work_branch: null
|
||||
work_branch: agent/codex/T-611
|
||||
write_paths:
|
||||
- README.md
|
||||
- docs/README.md
|
||||
- docs/tasks/T-611.md
|
||||
- core/catalog/
|
||||
- app-modern/ui/gio/
|
||||
@@ -91,3 +93,8 @@ write_paths:
|
||||
- 2026-07-18:代码图确认 `loadDisk` 已拒绝 symlink/非普通 entry,`loadUncached` 遇 `ErrIconCacheUnsafe` 直接返回且不 fetch;`classifyIconFailure` 已映射为 `unsafe_cache`,`IconFailedPayload` 已含安全的 reference/DPI,无需扩展 raw error 字段。
|
||||
- 2026-07-18:调用图确认 `IconEventDelivery.LoadAndPublish` 与双端 `IconFailure` 目前都只有测试调用;双端 `ApplyEvent` 只保存 failure code 且布局不读取。任务因此采用“真实 cache→event 集成测试 + UI 安全提示 + 人工 runbook”,明确不宣称生产装配已完成。
|
||||
- 2026-07-18:现有 `docs/api.md` 已冻结磁盘名 `<digest>-<dpi>.icon` 与 `unsafe_cache` 稳定枚举,架构只定义 `%LOCALAPPDATA%/OwnSoftBox/cache/` 逻辑根;T-611 将细化正式 icon root 与 locator 映射,同时保留 constructor root 才是当前代码事实。
|
||||
- 2026-07-18:新增 `docs/troubleshooting.md` 后治理闸门要求根 README 与 docs README 登记所有文档;将这两个纯导航文件补入 `write_paths`,不扩大功能范围。
|
||||
- 2026-07-18:core 新增真实目录与 Windows symlink cache entry 用例,证明 `ErrIconCacheUnsafe` 可识别、Fetcher 零调用、entry/外部 target 不删除不改写;真实 `IconCache → IconEventDelivery` 只发布一个 `IconFailed/unsafe_cache`,后台错误链保留 sentinel 且 event 不含 cache root/raw error。
|
||||
- 2026-07-18:modern/Win7 将 failure map 从单 code 升级为完整 `IconEventIdentity + IconFailureCode`,保持 `IconFailure(appID)` 兼容;详情只为当前 `unsafe_cache` 显示文字标题、fail-closed/人工恢复说明和安全 code/app/`<digest>-<dpi>.icon` locator。语义树回归覆盖普通失败不冒充告警以及新请求、迟到事件、ready、DPI/reference 变化、取消、删除和同引用 snapshot 生命周期。
|
||||
- 2026-07-18:新增不跟随链接的管理员 runbook,同步 API、路由、架构、编码规则和导航;明确安装目标 root 与当前生产 IconCache/Delivery/Fetcher 尚未装配的真实边界,未增加自动 delete/rename/quarantine/retry。
|
||||
- 2026-07-18:验证通过:`GOWORK=off go vet ./catalog`,`GOWORK=off go test -count=10 ./catalog`,modern Go 1.25.0 与 Win7 Go 1.20.14 各 `go test -count=10 ./ui/gio`;Windows symlink 子用例实际 PASS;`./scripts/verify_phase0.ps1`、agent-context、harness governance 与差异检查全部通过。
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 故障排查
|
||||
|
||||
本文只提供可审计的人工恢复步骤。应用不得把这里的操作包装成自动删除、重命名、隔离或重试按钮。
|
||||
|
||||
## 不安全图标缓存(`unsafe_cache`)
|
||||
|
||||
### 诊断含义
|
||||
|
||||
`unsafe_cache` 表示请求对应的磁盘缓存 entry 是 symlink、目录或其他非普通文件。`IconCache` 会立即 fail closed:不读取或跟随该 entry,不删除、改写或重命名它,也不继续远端获取。本次图标请求保持占位,其他软件列表功能不应被阻塞。
|
||||
|
||||
modern 与 Win7 详情只显示以下安全字段:
|
||||
|
||||
- 诊断码:`unsafe_cache`。
|
||||
- 应用 ID:Catalog 的稳定 app ID。
|
||||
- 缓存定位符:`<digest>-<dpi>.icon`,其中 digest 是已验证 `sha256:` 引用去掉前缀后的 64 位十六进制值,DPI 是 48~768 的已验证整数。
|
||||
|
||||
定位符不包含 cache root、原始 URL/query、后台 error、token 或 link target。不得要求用户从 UI 猜测这些内容。
|
||||
|
||||
### 确认缓存根
|
||||
|
||||
定位符对应的逻辑位置是:
|
||||
|
||||
```text
|
||||
<configured-icon-cache-root>/<digest>-<dpi>.icon
|
||||
```
|
||||
|
||||
安装模式正式装配的目标根是:
|
||||
|
||||
```text
|
||||
%LOCALAPPDATA%\OwnSoftBox\cache\icons\
|
||||
```
|
||||
|
||||
当前仓库尚未在生产 `cmd` 中装配 `NewIconCache`、`IconEventDelivery.LoadAndPublish` 或真实 `IconFetcher`;因此在该装配任务完成前,事实来源始终是调用方传给 `NewIconCache` 的 root,不能仅凭上述目标路径断定实际位置。测试临时目录也不是用户数据目录。
|
||||
|
||||
### 人工恢复步骤
|
||||
|
||||
1. 完全退出 SoftBox,并用受信任的系统管理工具确认 modern/Win7 客户端及相关后台进程均已退出。不要在应用仍可能访问缓存时处理 entry。
|
||||
2. 记录诊断码、应用 ID、缓存定位符、发生时间和客户端版本。不要复制 URL/query、凭据或未经验证的 link target。
|
||||
3. 从部署配置或未来的生产装配记录确认 configured icon cache root。确认它位于预期 OwnSoftBox 数据根内,其父目录可信,且 root 本身不是 symlink、junction 或其他 reparse point。无法确认时立即停止并联系管理员。
|
||||
4. 只从已核验 root 的父目录侧定位 root 和该 locator。不要打开可疑 entry,不要进入目录,不要解析、跟随或访问其 target;检查应使用不跟随链接的元数据能力。
|
||||
5. 由管理员按组织的事件响应/文件处置策略处理。可选择从可信父目录侧整体移走已核验的 icon cache 目录,或只处置精确 locator 对应的 entry;任何操作都必须作用于目录项本身且不得递归跟随链接。处理前保留第 2 步的安全诊断信息。
|
||||
6. 在可信父目录下重新建立空的普通 `icons` 目录后再启动客户端。生产图标装配完成后,后续 cache miss 才会通过已验证 Fetcher 重建普通缓存文件;当前仓库不能宣称重启已经具备该能力。
|
||||
7. 若相同 locator 再次出现 `unsafe_cache`,立即停止重复处理,保留新的安全诊断并升级给管理员调查 cache root 权限、外部写入者和部署配置。
|
||||
|
||||
### 禁止操作
|
||||
|
||||
- 不在应用内自动 delete、rename、quarantine 或 retry 可疑 entry。
|
||||
- 不提供或执行会递归遍历、跟随 reparse point、通配整个用户目录的清理命令。
|
||||
- 不通过打开 target 来判断其内容,不把绝对路径、target 或原始错误复制到 UI/事件。
|
||||
- 不把普通损坏文件、离线、hash/decode 失败当成 `unsafe_cache`;这些情况继续使用 `invalid_content` 或 `unavailable`。
|
||||
Reference in New Issue
Block a user