Skip to content

Add fail-closed ARM64 self-update artifact selection - #91

Merged
AviBackToBlack merged 4 commits into
mainfrom
codex/arm64-self-update
Sep 26, 2026
Merged

AviBackToBlack merged 4 commits into
mainfrom
codex/arm64-self-update

Conversation

@AviBackToBlack

@AviBackToBlack AviBackToBlack commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • select the Windows ARM64 release archive directly from Go's native GOARCH, while preserving the raw cb.exe selection and asset names for amd64
  • preserve legacy two-entry amd64 checksum manifests and require the canonical three-entry manifest for dual-architecture releases
  • stage and attest the selected ARM64 archive before extracting only the exact cb.exe from the canonical cb.exe / LICENSE / README.md layout
  • reject unsupported architectures, malformed/duplicate/unsafe archive entries, ZIP bombs, changed artifacts, and non-canonical URLs/layouts without fallback
  • document that this qualifies architecture selection/artifact handling only; real Windows-on-Arm + Docker Desktop E2E remains required before an ARM64 support claim

This unit starts from current main and is independent of the unmerged replacement transaction in #90.

Validation

  • go test -race ./...
  • go vet ./...
  • 11 Python wrapper/discovery tests
  • Windows amd64 full cross-build via go test -exec=/bin/true ./...
  • Windows arm64 full cross-build via go test -exec=/bin/true ./...
  • native Windows execution of the complete internal/selfupdate test binary, including ARM64 selection, staging ACLs, three-entry manifest validation, provenance boundary, exact archive extraction, and unsafe-entry rejection

Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread internal/selfupdate/verify.go
Comment thread docs/roadmap-implementation-requirements.md

@CherylSnowVeil CherylSnowVeil left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — ARM64 self-update artifact selection (9c1ebaf)

Initial review of the current head. I traced the implementation against the release workflow contract and the real published releases rather than relying on the PR description.

What I verified

  • Release contract alignment. .github/workflows/release.yml produces cb.exe (amd64), container-bin-<tag>-windows-{amd64,arm64}.zip, and a three-entry SHA256SUMS (cb.exe line is the amd64 raw binary), with attest-build-provenance covering cb.exe and *.zip. validateRelease, checksumManifestNames, and the attestation subject-digest check match this contract exactly.
  • Real releases. Published v1.0.0/v1.1.0 have only {cb.exe, amd64 zip, SHA256SUMS} — the checksumLayoutLegacyAMD64 path covers them (including the env-gated Windows integration test, which still pins v1.1.0, an immutable legacy release). An arm64 build against those releases fails closed with "missing required asset …windows-arm64.zip".
  • Selection (selfupdate.go). Arch gate happens before any network access; companion arm64 asset metadata (name/size/canonical URL) is validated even on amd64 so a malformed companion asset can't slip through — covered by TestPlanRejectsUnsafeCompanionARM64MetadataOnAMD64.
  • Staging (stage.go). Downloads the selected artifact (arm64 zip or amd64 cb.exe) + manifest with the existing canonical-URL/redirect/size guards; Staged.Cleanup layout guard extended correctly (base-name check, still confined to the staging-prefixed dir).
  • Verification (verify.go). Plan re-validation is consistent with staging's; checksum manifest verified before attestation; gh attestation verify bound to repo/workflow/ref/predicate with subject digest match; pre/post attestation re-hashes close the mutation window; extraction re-validates os.SameFile, enforces the exact {cb.exe, LICENSE, README.md} entry set, rejects duplicates/encryption/unsafe methods/non-regular entries, bounds total uncompressed size (overflow-safe ordering), enforces n == UncompressedSize64 on top of the zip CRC, creates cb.exe O_EXCL|0600 with the private DACL, then re-hashes both the extracted exe and the archive. The checksumLayout unexported field also means externally constructed Plan values fail closed in Stage/Verify.
  • CI is green on all jobs, including native Test and build (Windows ARM64), Format, vet, test (Linux), govulncheck, and reproducibility.
  • Docs (README, security-model, release-matrix, roadmap files) accurately describe the new behavior and correctly keep the ARM64 support claim gated on real Docker Desktop E2E.

Finding

  • 🟢 [nit] internal/selfupdate/verify_test.go — arm64TestArchive silently drops extras keys outside its fixed write list. The helper merges extras into entries but only writes names present in {"cb.exe", "LICENSE", "README.md", "../escape"}. Any future test passing a different extra name (e.g. to exercise the duplicate-entry or unsafe-method rejection) would produce an archive that doesn't contain it, leading to a confusing failure or an accidentally empty fixture. Consider iterating the extras map after the canonical entries (or t.Fatal on keys outside the write list).

Summary

The implementation is consistently fail-closed, keeps the amd64 path byte-compatible with legacy two-entry manifests, threads architecture selection through GOARCH without expanding the trust surface, and the new verification path (attest-before-extract, exact archive layout, bounded decompression, re-hash chain) is solid. Docs are accurate. One test-helper nit above; nothing blocking.

@AviBackToBlack

Copy link
Copy Markdown
Owner Author

Addressed CherylSnowVeil's latest test-helper nit in signed commit ca9a6fd. arm64TestArchive now writes every arbitrary extra entry after the canonical entries in deterministic sorted order, while still allowing canonical-entry overrides. The unexpected-entry regression now uses unexpected.bin, proving non-hardcoded extras are present. Full race tests, vet, and release-style version checks pass locally.

@AviBackToBlack
AviBackToBlack enabled auto-merge (squash) September 26, 2026 02:50
@AviBackToBlack

Copy link
Copy Markdown
Owner Author

Conflict resolved in signed merge commit 8278e26 after PRs #88 and #89 advanced main. The resolution keeps their merged roadmap updates, PR #91's ARM64 status, and the ca9a6fd test-helper fix. Full race tests, vet, version checks, both Python suites, and a full Windows ARM64 cross-build pass on the merged tree.

@AviBackToBlack
AviBackToBlack merged commit 8d2505f into main Sep 26, 2026
14 checks passed
@AviBackToBlack
AviBackToBlack deleted the codex/arm64-self-update branch September 26, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants