aps-agent/docs/development/packaging-smoke.md

163 lines
11 KiB
Markdown
Raw Permalink 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.

# Defender 冒烟与离线安装包完整性校验
> round-43 并行 sub agent 方向 CC(矩阵 99 / 118 本地可测部分)。
> 执行日期:2026-08-02;环境:Windows(PowerShell),Node v22.13.0。
## 1. 验收矩阵与本方向范围
| 矩阵行 | 条目 | 本地可测部分 | 剩余(外部项) |
|--------|------|--------------|----------------|
| 99 | Defender 和真实 APS 求解通过 | Defender 静态扫描冒烟(本机 MpCmdRun.exe,见 §3);真实 APS 求解通过由四引擎黄金测试覆盖(既有测试体系,非本方向交付) | Defender for Endpoint 门户/API 入 CI 门禁;实时防护真实拦截演练 |
| 118 | 干净离线机安装冒烟 | 离线安装包完整性校验:安装包存在/体积/SHA-256 校验和清单 + dry-run 安装路径(见 §4) | 干净离线机(无 Python、无网络)实机安装 + 冷启动 + 升级/回滚验收,需外部验证机 |
硬性纪律遵守情况:未修改 server/**、apps/desktop/**、packaging/**(仅只读读取 apps/desktop/package.json 与 packaging 配置推导产物路径);未 commit/push/merge;未重启 8003/5173。
## 2. 涉及文件
新增(本方向写范围):
- scripts/defender-scan.mjs — Defender 静态扫描冒烟
- scripts/offline-install-check.mjs — 离线安装包完整性校验
- tests/node/packaging-smoke.test.mjs — 两个脚本可测逻辑的 node 单测
- docs/development/packaging-smoke.md — 本文档
运行时产物(build/ 已被 .gitignore 忽略,不入库):
- build/offline-check/offline-install-manifest.json — 校验和清单(机器可读)
- build/offline-check/checksums.sha256 — sha256sum 格式校验和(可随安装包转移)
- build/offline-check/aps-agent-desktop-0.1.0.exe — portable 模式拷贝的安装包
只读引用的既有产物(构建于 2026-07-31,来源见下):
- dist/sidecar/aps-sidecar/aps-sidecar.exe — PyInstaller Sidecar(scripts/build-sidecar.mjs 产出)
- apps/desktop/release/win-unpacked/工业智核 APS.exe — electron-builder win-unpacked 冻结桌面包
- apps/desktop/release/win-unpacked/resources/sidecar/aps-sidecar.exe — 桌面包内置 Sidecar
- apps/desktop/release/aps-agent-desktop-0.1.0.exe — NSIS 安装包(electron-builder,artifactName aps-agent-desktop-0.1.0.exe)
## 3. Defender 静态扫描冒烟(scripts/defender-scan.mjs)
原理:调用本机 Windows Defender 命令行工具做按需静态扫描:
MpCmdRun.exe -Scan -ScanType 3 -File <path> -DisableRemediation
- -ScanType 3 = 自定义文件/目录扫描(目录递归)。
- -DisableRemediation = 只检出不清理,构建产物不会被删除/隔离(本脚本默认开启;如确有威胁,只报告不处理)。
- 退出码按任务约定区分:0 = 通过/无威胁;2 = MpCmdRun 不可用(跳过);非 0(1)= 检出威胁或扫描失败;另定义 3 = 无产物可扫。
用法:
node scripts/defender-scan.mjs # 自动发现并扫描 4 个产物
node scripts/defender-scan.mjs <file-or-dir> ... # 显式指定目标(支持目录递归)
node scripts/defender-scan.mjs --timeout 900000 # 单文件超时(默认 600000 ms,已覆盖百 MB 级安装包)
node scripts/defender-scan.mjs --mpcmdrun <path> # 严格指定 MpCmdRun 路径(不存在则 exit 2)
node scripts/defender-scan.mjs --json # 输出机器可读摘要
node scripts/defender-scan.mjs --no-copy-fallback # 关闭非 ASCII 路径的临时拷贝回退
node scripts/defender-scan.mjs --self-test # 解析器/产物发现自检(不调用真实扫描)
自动发现目标(由 apps/desktop/package.json 只读推导):sidecar-exe、desktop-frozen-exe、
bundled-sidecar-exe、nsis-installer。缺失目标打印 [MISSING] 并跳过。
已知行为(实测确认):
- MpCmdRun 无法解析某些非 ASCII 工作区路径:对原路径扫描可能失败
(node spawn 场景报 CmdTool: Failed with hr = 0x80508023,exit 2;PowerShell 调用场景报
"was skipped",exit 0)。本脚本默认将含非 ASCII 字符的文件拷贝到 ASCII 临时目录后扫描
拷贝(字节相同,结论等价),输出标记 [temp-copy];目录不拷贝、原位递归扫描。
- 威胁检出文本是权威信号(MpCmdRun 在 -DisableRemediation 模式下威胁时 exit 2 且打印
LIST OF DETECTED THREATS;默认清理模式威胁时 exit 0 但打印 found N threats),解析器两种
模式都覆盖,并以输出文本为准判定 clean/threat/skipped/error。
- 扫描结果只输出摘要(状态/威胁名/耗时/策略),不输出大段日志(--json 附 outputTail 便于排查)。
威胁输出解析的正确性已用真实 EICAR 测试文件验证(开发期探测):
- 默认模式:exit 0,输出 Scanning ... found 1 threats.,随后 Cleaning started/finished,文件被清除(约 21.5s)。
- -DisableRemediation 模式:exit 2,输出 LIST OF DETECTED THREATS + Threat : Virus:DOS/EICAR_Test_File,文件保留(约 79ms)。
- 脚本内不自带 EICAR 生成(避免污染本机 Defender 事件),解析用例固化在 --self-test 与单测中。
## 4. 离线安装包完整性校验(scripts/offline-install-check.mjs)
职责:NSIS 安装包(以及相关产物)SHA-256 校验和清单的生成与校验 + dry-run 安装路径检查。
用法:
node scripts/offline-install-check.mjs # 生成清单并立即校验 + dry-run
node scripts/offline-install-check.mjs --verify # 只校验既有清单
node scripts/offline-install-check.mjs --portable # 便携包:拷贝安装包到输出目录并生成相对路径清单
node scripts/offline-install-check.mjs --out-dir <dir> # 默认 build/offline-check
node scripts/offline-install-check.mjs --root <dir> # 解析清单路径的基准目录(默认仓库根)
node scripts/offline-install-check.mjs --localappdata <p> # dry-run 时覆盖 %LOCALAPPDATA%
node scripts/offline-install-check.mjs --json # 机器可读报告
node scripts/offline-install-check.mjs --self-test # 内部冒烟(临时文件,不依赖真实产物)
校验规则:
- 安装包存在性:缺失即 FAIL。
- 体积门禁:安装包 >= 50 MB,其余产物 >= 5 MB(PyInstaller/Electron 产物异常偏小视为失败)。
- 校验和:逐文件流式 SHA-256 与清单比对(存在/大小/哈希全匹配才 PASS;篡改文件会检出 mismatch)。
- dry-run 安装路径:由 apps/desktop/package.json(只读)的 productName 推导 NSIS per-user 默认安装目录
%LOCALAPPDATA%\Programs\<productName>,即 C:\Users\<user>\AppData\Local\Programs\工业智核 APS;
当前目录不存在时报告 dry-run 通过但注明需外部干净离线机实装;若已存在则校验 app 主程序与
resources/sidecar/aps-sidecar.exe 布局是否完整。
离线转移流程(便携包):
1. 构建机执行 node scripts/offline-install-check.mjs --portable
→ build/offline-check/ 内含安装包 + checksums.sha256 + offline-install-manifest.json(路径相对该目录)。
2. 将整个目录拷入 U 盘等介质,在目标离线机执行:
node scripts/offline-install-check.mjs --verify --root <介质目录>
3. 实装后用 dry-run 输出的预期路径核对安装落点。
说明:checksums.sha256 为 UTF-8 编码;中文 Windows 下 PowerShell Get-Content 可能按 GBK 显示乱码
(文件本身正确,可用 node 或 UTF-8 工具读取);机器可读校验以 JSON 清单为准。
## 5. 测试
- 脚本内自检:node scripts/defender-scan.mjs --self-test(8/8 PASS)、
node scripts/offline-install-check.mjs --self-test(7/7 PASS)。
- node 单测:node --test tests/node/packaging-smoke.test.mjs(12/12 PASS)。
覆盖:MpCmdRun 输出解析(clean/threat/skipped/计数/威胁名)、verdict 分类、安装包名模板解析、
产物发现、sha256 确定性、checksums 往返解析、预期安装目录、dry-run 布局检测、体积门禁、篡改检出。
## 6. 本地实测记录(2026-08-02)
Defender 扫描(MpCmdRun: C:\Program Files\Windows Defender\MpCmdRun.exe,签名 1.455.456.0):
[PASS] sidecar-exe dist/sidecar/aps-sidecar/aps-sidecar.exe (17.5 MB) clean 0.2s [temp-copy]
[PASS] desktop-frozen-exe apps/desktop/release/win-unpacked/工业智核 APS.exe (215.2 MB) clean 0.1s [temp-copy]
[PASS] bundled-sidecar-exe apps/desktop/release/win-unpacked/resources/sidecar/aps-sidecar.exe (17.5 MB) clean 0.2s [temp-copy]
[PASS] nsis-installer apps/desktop/release/aps-agent-desktop-0.1.0.exe (163.1 MB) clean 1.0s [temp-copy]
Verdict: PASS(exit 0,无威胁,skippedCount 0)
副产品验证:--no-copy-fallback 对非 ASCII 路径原位扫描 → 正确报失败(hr 0x80508023,exit 1),
佐证 temp-copy 回退是必要且正确的。
离线安装包校验:
generate(4 条目): PASS — nsis-installer / sidecar-exe / desktop-frozen-exe / bundled-sidecar-exe
verify(复用清单): PASS
portable 便携包 : PASS(build/offline-check/ 内含 aps-agent-desktop-0.1.0.exe 171,025,378 B)
verify --root build/offline-check(模拟介质目录): PASS
dry-run 安装路径 : C:\Users\<user>\AppData\Local\Programs\工业智核 APS — 当前机器未安装(存在性 false),
实机安装需外部干净离线机
SHA-256 清单(与 build/offline-check/offline-install-manifest.json 一致):
nsis-installer 044c4a83aaf28476a1e2405a7a08185b5aff0dfab27f0299b8bcf16c8d32bf21
sidecar-exe 88abc8b1ae0d4bbd715ee9a263cf107198c183b040728711c28c1ae65a8e8f39
desktop-frozen-exe f5b4b22b3c7bd0dbb5708d90238b744f7b51f8ef682081b2458732954cf6cef4
bundled-sidecar-exe 88abc8b1ae0d4bbd715ee9a263cf107198c183b040728711c28c1ae65a8e8f39
产物来源说明:全部产物为 2026-07-31 既有构建(scripts/build-sidecar.mjs 产出 Sidecar;
electron-builder 产出 win-unpacked 与 NSIS 安装包,apps/desktop/release/),本次未重建、未修改。
## 7. 矩阵达成情况与剩余风险
矩阵 99(Defender + 真实 APS 求解):
- 本地部分达成:Defender 静态扫描冒烟 4/4 产物 clean;解析器经真实 EICAR 输出格式验证。
- 真实 APS 求解通过:由四引擎黄金测试覆盖(既有体系,本方向不重复交付)。
- 剩余(外部):Defender for Endpoint 门户/API 入 CI 门禁;实时防护真实拦截演练(本机实时防护已开启,
但真实拦截需受控样本与策略配合)。
矩阵 118(干净离线机安装冒烟):
- 本地部分达成:安装包存在/体积/校验和/清单生成与校验/dry-run 路径全部通过。
- 剩余(外部):干净离线机(无 Python、无网络)实机安装 + 冷启动 + 升级/回滚验收;需外部验证机,
可用 §4 portable 流程随 U 盘执行。
已知限制:
- 本机非管理员,无法读取 MpPreference 排除项;若存在路径排除,Defender 会 skipped(exit 0 + WARNING),
需在门禁前人工复核排除策略。
- 扫描为静态按需扫描(-ScanType 3),不等价于实时防护全链路;安装包未签名(既有事实,需签名证书)。