Safe Haven 中文文档 下载 App

Safe Haven 提交流水线状态机,七个状态严格顺序推进

为什么要做状态机

Safe Haven 的提交流水线是一个多服务协作的异步流程:开发者上传 APK(经过 staging)→ 扫描器拉取并处理 → 结果回传 → 状态推进 → 进公开目录。如果不用明确的状态机来串,很容易出现:

Safe Haven 用一个严格顺序的状态机来解决这个问题——每个状态只从一个前置状态推进而来,有明确的触发条件和可能的下一个状态。

七个状态和转换

pending_upload
    ↓ 文件到达 staging
pending_scan
    ↓ 扫描器拉取
scanning
    ↓ 扫描完成且通过
pending_review
    ↓ 自动批准定时器触发
live
    
scanning ──→ rejected(扫描检出恶意 / 结构验证失败)
live ──→ retired(复扫发现已存在相同 APK 哈希或过时版本代码)
状态含义进入条件可能的下一个状态
pending_upload条目创建,等文件到达开发者注册应用或同步进来后pending_scan
pending_scan文件到 staging,等扫描器处理APK 成功到达 staging 临时目录scanning
scanning扫描器正在处理扫描器从队列拉到任务live / rejected
pending_review扫描通过,等自动批准扫描完成且所有规则通过live /(等待定时自动批准)
live进生产目录,对外可见pending_review 定时器触发retired
rejected扫描未通过,不进目录scanning 时检出问题(终态,需要重提)
retired已上架版本被淘汰复扫发现重复 APK hash / 过时 versionCode(终态)

各状态的细节

pending_upload

条目刚创建出来,数据库里记录了基本信息(包名、版本名、版本代码、来源),但 APK 文件还没到。这个状态锁定版本参数——versionCode 和 versionName 在这一步就固定了,后面扫描时如果发现 manifest 里的值不同,服务器会自动覆盖修正。

pending_scan

APK 文件已经成功上传到 staging 目录(临时存储),数据库记录里填上了 APK 的 staging 路径。扫描队列里这个条目等待 safehaven-scanner 拉取。

pending_upload → pending_scan 的转换由文件上传完成事件触发——后端监控 staging 目录,文件到达后自动把条目推入扫描队列。

scanning

扫描器从队列拉到任务后,先把条目标记为 scanning——防止其他 worker 实例重复拉取同一个 APK。然后开始:

  1. 下载 staging 里的 APK
  2. 计算 SHA-256 哈希、比对重复
  3. 提取 manifest 里的 versionCode、包名
  4. 提取签名哈希,比对签名锁定基准
  5. 跑 VX-TITANIUM 引擎(如果可用)
  6. 查本地威胁数据库

处理完成后回传结果,状态推进。如果扫描器崩溃或超时没回传,条目会卡在 scanning 状态(需要后台超时清理脚本处理)。

pending_review

扫描通过后进入这个状态。它不是等人手动审核——Safe Haven 有 cron 定时任务在后台跑,检查 pending_review 条目:

这个状态的意义是安全缓冲区——即使扫描器误判通过了恶意 APK,pending_review 期间如果有其他监控发现异常,还能在进公开目录前拦住。

live

进入生产目录(apps/{包名}/{versionCode}/),全局索引更新,客户端下次同步时就能看到。

rejected

扫描管线检出问题,直接进入 rejected。触发条件:

rejected 是终态——需要开发者手动修正后重新提交(比如换 versionCode、换签名、修正包名)。

retired

这是 live 之后的回退状态。触发条件通常在复扫管线里:

retired 的条目会从全局索引里移除,客户端下次同步时会从本地删除对应的版本记录。

状态回滚

状态机是单向推进的,没有自动回滚机制。如果扫描器误判把一个良性 APK 推进了 rejected:

  1. 管理员手动把 rejected 状态改成 pending_scan 或 pending_review
  2. 让扫描器重新处理
  3. 或者直接强制改成 live(Admin 权限)

应用条目的独立状态

除了提交的七个状态,应用条目本身还有一个独立状态字段:

这个状态和提交状态是两回事——应用条目的 Suspended 不影响已有的 live 提交,需要单独处理。