# UtiliVera Scan 0.5.1 安装器独立审核（Round 2）

审核日期：2026-08-20  
审核角色：与 Round 1 相同的独立安装/发布审核员  
审核范围：最终冻结候选及 Round 1 内部 P1 整改；只读核验，未修改产品、安装器或证据

## 结论

**内部安装工程：PASS（P0=0、内部 P1=0）。正式公开下载：BLOCKED（外部 P1=2）。P2=6。**

Round 1 的第三方许可/SBOM P1 已关闭。最终 202-file payload 中的 174 项第三方文件全部唯一映射到 28 个组件；15 份完整离线许可证、3 份对应源码和 22 份 notices 均有固定身份。上一轮复核发现的 GPL-2.0-only JBIG 硬风险也已实质消除：`libjbig-0.dll`、GPL-2.0 条目及源码已从冻结包移除，替换为 `jbig=OFF` 的可复现 libtiff 4.6.0；32 个打包 PE 的 import table 和原始符号扫描均未发现 `libjbig`、`jbg_dec` 或 `jbg_enc`。

内部 PASS 不等于公开发布批准。最终安装器与主 EXE 仍为 `NotSigned`，产品与版本 URL 仍为 404。可以部署不含二进制、Offer、`downloadUrl` 或可用下载按钮的预发布网站；当前候选不得正式公开下载，也不得启动 Scan 的连续 30 天 1,000 有效下载计时。

## 1. 最终冻结身份：PASS

本轮直接对当前文件独立计算：

| 对象 | 大小 | SHA-256 | Authenticode |
|---|---:|---|---|
| `UtiliVeraScan.exe` | 347,136 bytes | `EA12A7B0D89091907255E3FFC36F2638066EC4BCA33FAC7508E8EA8DAB7B1011` | `NotSigned` |
| `UtiliVeraScan-Setup-0.5.1-Windows-x64.exe` | 102,801,116 bytes | `1B41E2AAEF33C226C21CF8F8742C234F78F69ACF67FFBCF8808DB4C2E2A1DDB9` | `NotSigned` |

`SHA256SUMS.txt`、build evidence、test evidence 和 10/10 stability evidence 均引用上述最终 installer 身份。此前整改过程中的 102,800,441-byte / `6726B8...AFEA5` 只是中间候选，未混入最终门禁，也不在本轮批准范围内。

主 EXE 的 FileVersion/ProductVersion 继续为 `0.5.1.0`，冻结 SHA 未因安装许可整改漂移。

## 2. 202-file payload：PASS

本审核员重新以当前 `dist` 为基准逐项计算，而不是接受 build JSON 自报：

- manifest entries：202
- 当前 payload files（排除 `dist/installer`）：202
- missing：0
- extra：0
- bytes mismatch：0
- SHA-256 mismatch：0
- manifest frozen EXE SHA：`EA12A7...B1011`

安装器 build gate 还会在实际安装后逐项验证全部 202 个 payload 文件；最终 evidence 为 `embeddedManifestVerified=true`、`validationFailuresBeforePass=0`。因此许可证、源码、recipe 和自编 libtiff 都确实进入冻结候选，不是仅存在于源码树外的审核附件。

## 3. Round 1 内部 P1：第三方许可/SBOM——CLOSED

独立运行 `test-third-party-sbom.ps1` 得到：

| 项目 | 独立结果 |
|---|---:|
| components | 28 |
| expanded third-party files | 174 |
| unmapped | 0 |
| multi-mapped | 0 |
| pinned full license texts | 15 |
| source archives | 3 |
| notice/license files | 22 |

每个展开行都带 relative path、bytes、SHA-256、component、version、copyright、license、license files、source、source revision 和 notice。没有继续使用“有一个非空 notices 文件就算完整”的旧逻辑。

15 份 `third-party-licenses` 文件的本地 SHA 与 SPDX license-list-data v3.27.0 对应官方原文逐项一致；Apache、MIT、BSD、CC0、GCC Runtime Exception、GPL-3、IJG、LGPL、libpng、libtiff、Zlib、ZPL 等均可离线查看。SBOM 为每个组件保留明确版权字段和固定许可路径。

3 份源码均由本审核员重新计算本地 bytes/SHA，并与其声明的正式上游 URL 返回字节比较，三者完全一致：

| 源码 | bytes | SHA-256 | 上游字节匹配 |
|---|---:|---|---|
| libtiff 4.6.0 | 3,584,534 | `88B3979E...5D99A` | PASS |
| GNU libiconv 1.17 | 5,413,283 | `8F74213B...71313` | PASS |
| libthai 0.1.29 | 417,728 | `FC80CC7D...F4BCD` | PASS |

`THIRD-PARTY-NOTICES.md`、`THIRD-PARTY-LICENSES.txt`、SBOM、15 个完整 license、原组件 notices 和 3 份源码都由安装通配规则带入安装目录，test evidence 也在实际安装后再次运行同一映射/许可/源码门禁。

**Round 1 P1-1 判定：CLOSED。**

## 4. JBIG 许可/依赖硬风险：CLOSED

上一版包中 `tesseract.exe → libtiff-6.dll → libjbig-0.dll` 的直接导入链已不存在。本轮证据：

- `libjbig-0.dll` 不在 202-file payload；
- SBOM 不再包含 JBIG-KIT 或 GPL-2.0-only 组件；
- 当前 `libtiff-6.dll`：748,455 bytes；
- 当前 libtiff SHA：`C8DB0929714C40B4CD7C5BF9B0DFF8896A4440417B8267699235F09CEA6D201A`；
- PE TimeDateStamp：0；
- libtiff 原始字节中 `libjbig`、`jbg_dec`、`jbg_enc` 匹配均为 0；
- 打包 `assets` 下 32 个 EXE/DLL 全部解析成功，共 135 个 import rows；任一 PE 出现 JBIG import 或符号都会令门禁失败，本轮为 0；
- `tesseract --version` exit 0，识别 libtiff 4.6.0 和其余 OCR runtime。

随包的 libtiff 源码、build recipe 和 build JSON 明确使用：

- libtiff 4.6.0 official source SHA `88B397...5D99A`；
- MSYS2 UCRT64 GCC 16.2.0 / CMake 4.4.2 / Ninja 1.13.2；
- `-Djbig=OFF`；
- `-Wl,--no-insert-timestamp`；
- 两次 clean reproduction、unique output hashes=1；
- 唯一输出 SHA `C8DB...201A`；
- direct imports 只含 Windows/UCRT、`libjpeg-8.dll` 和 `zlib1.dll`，无 JBIG。

这不是只改 SBOM 标签，而是替换了实际 DLL 字节并建立 source→recipe→build evidence→frozen output→全 PE 扫描的技术闭环。上一轮发现的 GPL/JBIG 硬 P1 判定为 **CLOSED**。

## 5. 产品与 OCR 回归：PASS

最终 product evidence 与冻结主 EXE 身份一致，记录 51 total / 51 passed / 0 failed。覆盖：

- 图像处理、deskew、清理、OCR、PDF/PDF-A、安全与 Unicode；
- 10 个 OCR 模型实际执行；
- 100-page、500-file、并发事务和 300/600-DPI 性能；
- 批量回滚、原子输出、工程/profile 历史；
- 10 种 UI 字典完整性。

本轮还直接执行打包 `tesseract.exe --version`，exit 0，确认自编 libtiff 替换没有破坏 OCR runtime 启动。51/51 结果生成时间晚于最终安装矩阵，且 frozen EXE SHA 与本轮一致。

## 6. 10 语言安装与 .NET 4.8 负向矩阵：PASS

最终 test evidence 引用 102,801,116-byte / `1B41...DDB9` 候选，记录：

- 10/10 language silent installs exit 0；
- 10/10 language silent uninstalls exit 0；
- 10/10 language install directories removed；
- 10/10 `/SIMULATENODOTNET` 均非零退出（均 exit 1）；
- 10/10 negative results 均 `payloadCopied=false`；
- 23 份持久日志存在且非空，总计 1,265,298 bytes。

本审核员复查 10 份负向原始日志：全部包含 `InitializeSetup returned False; aborting`，没有 `Starting the installation process`、`Dest filename` 或 `Installing the file` 信号。因此“复制前阻断”由 Inno 阶段日志支持，不只是 JSON 布尔值。

主 silent install、installed EXE hash、OCR language enumeration、同版 repair、silent uninstall、安装目录/HKCU-HKLM registration/开始菜单残留继续全部 PASS；uninstall 原始日志以 `Uninstallation process succeeded`、`Removed all? Yes` 结束。

## 7. 最终 10/10 安装器确定性：PASS

最终 stability evidence 逐行核验：

- requested/passed：10/10；
- result rows：10；
- non-zero exit：0；
- installer missing：0；
- unique hashes：1；
- deterministic：true；
- 10 行唯一 SHA 均为 `1B41E2AAEF33C226C21CF8F8742C234F78F69ACF67FFBCF8808DB4C2E2A1DDB9`。

10 次从 21:50 至 22:09 连续完成，每次 build 均重新执行 202-file 安装后验证和第三方门禁；最终 `SHA256SUMS.txt` 与该唯一 SHA 一致。

## 8. 外部正式发布 P1

### 外部 P1-1：安装器与主 EXE 未签名——OPEN

本轮独立实测两者 Authenticode 均为 `NotSigned`，违反仓库 `AGENTS.md` 的 public build code-signing 硬门禁。

**可测验收标准：**使用受信代码签名证书签署主 EXE 和最终 Inno 包并添加 RFC 3161 时间戳；在干净 Windows 10/11 上验证两者 `Get-AuthenticodeSignature` 均为 `Valid`。签名改变文件字节，必须重新冻结最终 bytes/SHA、202-file manifest、第三方门禁、10/10 build、10 语言及完整安装/卸载矩阵，并由同一独立审核员复核。

### 外部 P1-2：生产产品/版本 URL 未部署——OPEN

本轮独立 HTTPS GET：

- `https://utilivera.com/scan/` → 404；
- `https://utilivera.com/scan/releases/0.5.1/` → 404；
- `https://utilivera.com/support/` → 200；
- stable/versioned 两条 Scan installer 下载路径均为 404。

下载路径 404 是当前未签名候选没有误公开的正确安全状态；产品/版本页 404 则意味着安装器固定 URL 尚未激活。

**可测验收标准：**先部署无二进制产品页和版本页，两个 URL 均 HTTPS 200，并准确展示当前冻结证据、`BLOCKED_NOT_SIGNED` 与 `publicDownloadAvailable=false`；不得包含二进制、Offer、`downloadUrl` 或可用下载按钮。签名后再更新为最终签名 bytes/SHA，并只开放同一独立审核员批准的 stable/versioned 包。

## 9. 无二进制预发布边界

安装器内部工程已经具备无二进制网站预发布的前置条件，但本轮两个 Scan 页面尚未部署，故判定为：

**ELIGIBLE, NOT YET DEPLOYED。**

无二进制预发布不受代码签名阻断，但必须满足：

- 页面只发布产品、测试、许可和安装审核证据；
- 明确签名/下载仍阻断；
- 不复制当前 `1B41...DDB9` 未签名 installer；
- 不输出下载 Offer、下载 URL、可用 CTA 或会启动 30 天计时的正式 release 标记。

## 10. 非阻断 P2

1. **跨版本升级：**固定 AppId 和 `UsePreviousAppDir=yes` 具备升级基础，但仍只有同版 repair，没有真实 0.5.0→0.5.1、降级拒绝/回滚及用户设置迁移矩阵。
2. **权限范围：**默认 lowest/current-user 已覆盖；管理员 override 后的 Program Files、HKLM、all-users 快捷方式和卸载所有权尚无完整自动化矩阵。
3. **桌面快捷方式：**`/TASKS=desktopicon` 的创建、目标、工作目录与卸载清理仍未自动验证。
4. **用户数据披露：**`%LOCALAPPDATA%\UtiliVera\Scan` 保留策略主要在内部 evidence，最终用户可见 README/卸载说明仍应列出保留内容与手动清理方法。
5. **语言卸载原始日志：**10 种语言均验证 uninstall exit 和目录移除，但没有逐语言持久化独立 uninstall log。
6. **libtiff 可复现原始日志：**已有完整 source、recipe、机器可读 build JSON、两次唯一输出 SHA 和最终 PE 门禁；建议再保留两次 clean build 的独立 compiler/CMake 原始日志与环境包版本锁，增强第三方 DLL 可复现证据的可追溯性。

## 11. 门禁摘要

| 门禁 | Round 2 判定 |
|---|---|
| 最终 installer/EXE bytes、SHA、版本 | PASS |
| 202/202 payload 独立复算 | PASS（0 mismatch） |
| 28 组件 / 174 第三方文件 | PASS（0 unmapped / 0 multi） |
| 15 完整许可证 / 3 源码 / 22 notices | PASS |
| 自编 libtiff 4.6.0 `jbig=OFF` | PASS |
| 32 PE / 135 imports / JBIG symbols | PASS（0 forbidden） |
| 51/51 产品与 OCR 回归 | PASS |
| 10 语言安装/卸载 | PASS |
| 10 语言 .NET 4.8 负向门禁 | PASS |
| 23 份持久日志 | PASS |
| 最终 10/10 deterministic build | PASS（唯一 `1B41...DDB9`） |
| Round 1 第三方许可 P1 | **CLOSED** |
| Scan 产品/release URL | **外部 P1，404** |
| installer/main EXE 签名 | **外部 P1，NotSigned** |
| 无二进制预发布 | **ELIGIBLE, NOT YET DEPLOYED** |
| 正式公开下载 | **BLOCKED** |

## 最终判定

**UtiliVera Scan 0.5.1 安装器 Round 2：内部安装工程 PASS。P0=0，内部 P1=0，外部 P1=2，P2=6。**

Round 1 的许可/SBOM 缺口及复核中发现的 JBIG 组合风险均已通过真实 payload 替换和可重复门禁关闭。当前安装工程可作为无二进制预发布证据，但 `1B41...DDB9` 安装器与 `EA12...B1011` 主 EXE 仍未签名，产品/版本 URL 仍未部署，因此不得正式公开下载，30 天下载目标计时不得启动。
