fix(release): attest and verify desktop updates end to end - #4008
Conversation
bfd0cc7 to
24503ff
Compare
d0e7bb2 to
fc56ed0
Compare
|
Right design, and the cryptographic half is solid: the SAN regex is anchored and escaped so no other workflow can satisfy it, the issuer is strictly compared, the trust root comes from the bundled TUF seed rather than the attestation, and P1 — P2 — the attestation is signed before anything verifies it. Order in P2 — an env var disables verification in a signed production build, and the code claims otherwise. P2 — coverage went down while the trust chain went up. P2 — docs contradict the code. Suggestion: Minor: the record has no AI-assisted review: five subagents across the client path, workflow authority, removals, record binding and verifier scripts; I re-verified every finding above against head 简体中文设计方向正确,密码学部分扎实:SAN 正则锚定且转义,别的 workflow 满足不了;issuer 严格比较;信任根来自打包的 TUF seed 而非 attestation 自身; P1 —— P2 —— attestation 在任何验证之前就签发了。 P2 —— 一个环境变量能在签名的生产构建里关掉验证,而代码声称不会。 P2 —— 信任链变长,覆盖率却变低。 P2 —— 文档与代码矛盾。 建议: 次要:publication record 没有 |
Generated-by: OpenAI Codex
Generated-by: OpenAI Codex
fc56ed0 to
31ff980
Compare
EnglishThanks for the detailed review. The current head (
The deleted artifact micro-tests remain deleted. The integrated publication test covers the durable integrity boundary, including tampered bytes, while the workflow contract test now fixes the security-relevant order and validated-output flow. Restoring per-throw and staging implementation tests would add maintenance without strengthening the merge decision. The publication record also does not add a second Validation: rebuild, format, lint, Desktop typecheck, the focused release tests, and 简体中文感谢详细审查。当前 head(
已删除的 artifact 微型测试不会恢复。集成 publication 测试覆盖持久完整性边界,包括字节篡改;workflow contract 测试现在固定安全相关的执行顺序和已验证输出传递。为每个 throw 或 staging 实现细节恢复测试只会增加维护成本,不会增强合并判断。 Publication record 也不会增加第二个 本地已通过 rebuild、format、lint、Desktop typecheck、聚焦 release 测试和 |
Astro-Han
left a comment
There was a problem hiding this comment.
Approve at head 31ff9805c.
First, a correction to my own grading. I filed the raw workflow-input interpolation in the OIDC-capable publish job as P1, and you are right that the path was not reachable: validateStageRun (release-cli-publication.mjs:130) rejects any expectedVersion that differs from the checked-in release record, and the run identity is regex-gated to a positive integer before the inspection job emits anything. A version carrying shell metacharacters cannot equal a record value the attacker does not control, so the dependent job fails first. That is a multi-premise path, which puts it at P2 by my own rubric, not P1. The finding was still worth fixing - its safety rested on a non-local argument about a different job, so a future edit to inspect could have opened it silently - and the fix you shipped is better than what I asked for: consuming needs.inspect.outputs.* rather than merely quoting the raw inputs.
Verified at this head:
- Ordering is now correct.
verify-publication(:263) runs beforeactions/attest(:275), not after, so the exact-set and digest check against the immutable record happens before anything is signed. - The issued provenance is verified before publication, which I did not ask for and which materially strengthens the boundary.
gh attestation verify(:287) pins an exact--cert-identitystring rather than a pattern, pins the Actions OIDC issuer, iterates every file underproduct-release, and fails explicitly when nothing was verified - closing the empty-findhole that would otherwise pass silently. experiment-macos-autoupdate.mjsis gone. The remainingpackage-macos-autoupdate-next.mjsandverify-macos-autoupdate.mjsare the macOS counterparts of the Windows path and belong here.
The three you declined, I accept:
- The
MAKA_UPDATE_TEST_FEEDexception. Your point that gating onmakaUpdateTestProfilewould break the real candidate path - because the candidate is deliberately the exact unmarked release artifact - is the argument I did not have. Combined with the fact that setting that variable already requires same-user code execution, there is no privilege boundary to gain. - Not restoring the deleted micro-tests. The integrated publication test covers the durable integrity boundary including tampered bytes, and the workflow contract test now pins the security-relevant ordering. Per-throw tests would be maintenance without changing the merge decision.
- No second
channelfield. The validated version already derives the channel uniquely; storing both would be duplicate state.
AI-assisted review disclosure: Claude Code re-read the finalize workflow and the publication authority at this head; I verified the step ordering, the certificate-identity pinning, the version-validation chain that makes the original P1 unreachable, and the experiment removal against current main myself.
English
Summary
Make Desktop auto-updates and update-artifact publication one source-bound, independently verifiable contract:
main, verify the exact Release run artifacts and live Draft, and retain the protectedproduct-releaseEnvironment as the sole human publication gate;actions/attestto generate SLSA provenance for the verified update artifacts, publishingMaka-<version>-attestation.sigstore.jsonbeside them without a stored signing key or repository-administration credential;product-releaseconcurrency group and allow only stable, alpha, and beta product channels.The local update harness remains loopback-only and intentionally isolates updater mechanics from provenance because it serves synthetic bytes. Ordinary packaged launches have no feed override and always perform provenance verification. Finalize verifies the exact publication input before attestation and verifies the freshly issued provenance for every subject before publication. Production signing, notarization, hardened runtime, and Developer ID behavior remain unchanged.
Release authority
approved ASF source release → exact Release run/attempt → publication record + exact update artifacts → current verifier on reviewed main → protected environment approval → OIDC/Sigstore attestation → update transport → client-side identity + digest verificationASF release compliance and automatic-update trust are separate boundaries, and ASF release scope remains source-only. GitHub Release mutability can cause unavailability, but it cannot authorize different update bytes. Repository immutable releases and an Administration-read GitHub App are therefore neither required nor part of the publication contract.
Verification
npm run format:checknpm run lintnpm run rebuildnpm --workspace @maka/desktop run typechecknpm run check:release— 110 tests passednpm run check:third-party-noticesgit diff --check origin/main...HEAD中文
摘要
将 Desktop 自动更新与更新产物发布收敛为一个绑定源码、可独立验证的契约:
main运行,验证精确 Release run 构建产物与 live Draft,并保留受保护的product-releaseEnvironment 作为唯一人工发布门;actions/attest为已验证的更新产物生成 SLSA provenance,并发布Maka-<version>-attestation.sigstore.json,无需保存签名私钥或仓库 administration credential;product-releaseconcurrency group,产品 channel 只允许 stable、alpha 与 beta。本地更新 harness 仍严格限于 loopback;由于使用合成字节,它有意将 updater 机制与 provenance 分开验证。普通打包应用启动时不存在 feed override,始终执行 provenance 验证。Finalize 在签发前验证精确 publication input,并在发布前验证每个 subject 的新签发 provenance。生产签名、公证、hardened runtime 与 Developer ID 行为保持不变。
发布权威
已批准 ASF 源码 release → 精确 Release run/attempt → publication record + 精确更新产物 → reviewed main 上的当前 verifier → 受保护环境审批 → OIDC/Sigstore attestation → 更新传输 → 客户端验证身份与摘要ASF release 合规与自动更新信任是两个独立边界,ASF release 的范围始终仅限源码。GitHub Release 的可变性可能造成不可用,但无法授权不同的更新字节。因此仓库 immutable releases 和 Administration-read GitHub App 都不是必需条件,也不属于发布契约。
验证
npm run format:checknpm run lintnpm run rebuildnpm --workspace @maka/desktop run typechecknpm run check:release— 110 项测试通过npm run check:third-party-noticesgit diff --check origin/main...HEADAI use
Select exactly one:
Tool(s) and scope: OpenAI Codex implemented the release/update contract, attestation verification, tests, workflows, documentation, and release update harnesses.
Checklist
Does this PR entail a change in behavior?