You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Updater: sign release assets in CI and verify before install (no certificate needed) #104
The auto-updater (follow-up to #75) downloads release executables and offers to run them with no authenticity check:
MainWindowViewModel.InstallUpdate launches the downloaded file via Process.Start after the confirm dialog without consulting the Verified flag that AutoUpdateService2 computes.
The release workflow never generates .sha256 assets, so the checksum lookup in AutoUpdateService2 always comes back "no checksum" and the flow proceeds anyway.
Even with checksums, they'd come from the same channel as the binary — integrity only, not authenticity.
The asset is written to a predictable temp path (Path.GetTempPath() + asset filename). On Linux that's shared /tmp; on Windows any same-user process can swap the file between download and launch, and the installer then runs UAC-elevated (PrivilegesRequired=admin).
No version comparison — "latest" is fetched unconditionally, so a rolled-back/compromised release channel can push a downgrade.
The legacy AutoUpdateService (v1) is still live, has no verification logic at all, and its UpdateDownloaded event feeds the same install path as v2.
Proposed fix (no certificate, $0)
Authenticity via a plain keypair — this is independent of Authenticode/SmartScreen, which is deliberately out of scope here:
Keygen (once): ECDSA P-256 keypair. Private key → GitHub Actions secret (e.g. RELEASE_SIGNING_KEY). Public key (SPKI, base64) → embedded constant in the app.
CI (release.yml): after artifacts are built, sign each one (openssl or a small dotnet script) and upload <asset>.sig as a sibling release asset.
Updater:
download asset + .sig, verify with ECDsa.ImportSubjectPublicKeyInfo + VerifyData (in-box .NET, no new packages);
Verified becomes mandatory — no valid signature, no install UI (fail closed, including when the .sig asset is missing);
re-verify the file bytes immediately before Process.Start to close the temp-swap window; prefer downloading into a private app-data subdirectory instead of temp;
compare the release version against the running version and skip same/older releases.
Cleanup: delete the v1 AutoUpdateService; keep only AutoUpdateService2 wired to the UI.
Key rotation = update the CI secret and ship a release embedding the new public key.
Out of scope / later
Authenticode signing & SmartScreen reputation (SignPath Foundation, winget, MS Store, Azure Trusted Signing) — may be explored later; nothing here blocks it.
Flathub-distributed builds already get channel integrity; the sideloaded .flatpak bundle in releases is covered by the same .sig scheme.
Origin: app security review (branch claude/app-security-review-360c96) — this was the highest-severity finding.
AutoUpdateService (src/EDNexus.App/AutoUpdateService.cs:131-160,211-237) already does best-effort SHA-256 checksum verification against a .sha256 sidecar asset when one is present, but .github/workflows/release.yml has no step that generates/publishes checksums for release assets, so the verification code currently has nothing to check against in practice. Leaving open — needs the CI-side checksum (or signing) step this ticket asks for.
Deprioritizing this: signing + verifying release assets without a real cert is a maintenance burden (key rotation, keeping the embedded public key and CI secret in sync, updater fail-closed behavior to get right) that isn't proportionate to the risk for now.
Not a milestone requirement — leaving this open as a nice-to-have / backlog item, not something blocking a release. The underlying gaps it documents (no verification before Process.Start, predictable temp path, no version/downgrade check) are still worth fixing individually if anyone wants to pick off a piece, but the full signing scheme isn't something we're committing to right now.
Opened #159 for the specific gap the audit flagged: AutoUpdateService's checksum verification already existed but had no .sha256 sidecar to check against because release.yml never generated one. That PR just adds the CI step to publish sidecars — it does not implement the full signing scheme this issue describes, which stays deprioritized per the discussion above.
Status after the post-merge review (PRs #159 and #174): most of the gaps this issue documents are fixed; the signing scheme itself is still not done.
Fixed:
Verification is now mandatory before the install button appears: a missing .sha256 sidecar blocks the download and a mismatch deletes the file. (The old comparison was case-mismatched and never matched; Verified was ignored by InstallUpdate.)
The installer is re-hashed immediately before launch, from the same path that was shown in the confirm dialog.
Downloads go to a per-user app folder (%LOCALAPPDATA%\EDNexus\updates), not a predictable temp path.
Version comparison with proper SemVer (prerelease aware): same/older releases are skipped.
The legacy v1 service is gone; UpdateChecker/SemVer in Core is the single, unit-tested implementation. No ambient GITHUB_TOKEN is sent; size caps; exact asset-name matching; Linux/macOS are not self-updated.
release.yml publishes .sha256 sidecars, validates the version input, uses least-privilege permissions and waits on tests.
Still open (why this stays open, as a backlog item): a checksum from the same release proves integrity, not authenticity. There is an optional, inactive Authenticode step in release.yml (needs WINDOWS_SIGNING_CERT_PFX_BASE64 / WINDOWS_SIGNING_CERT_PASSWORD), but the ECDSA keypair scheme proposed here is not implemented and the updater does not yet require a signature.
Status from a codebase review on 2026-10-09 (post-merge review PRs #169-#177, which are now merged).
Activity
demortes commentedon Sep 10, 2026
Status from a codebase audit — partially done:
AutoUpdateService(src/EDNexus.App/AutoUpdateService.cs:131-160,211-237) already does best-effort SHA-256 checksum verification against a.sha256sidecar asset when one is present, but.github/workflows/release.ymlhas no step that generates/publishes checksums for release assets, so the verification code currently has nothing to check against in practice. Leaving open — needs the CI-side checksum (or signing) step this ticket asks for._Generated by Claude Code
Generated by Claude Code
demortes commentedon Sep 27, 2026
Deprioritizing this: signing + verifying release assets without a real cert is a maintenance burden (key rotation, keeping the embedded public key and CI secret in sync, updater fail-closed behavior to get right) that isn't proportionate to the risk for now.
Not a milestone requirement — leaving this open as a nice-to-have / backlog item, not something blocking a release. The underlying gaps it documents (no verification before
Process.Start, predictable temp path, no version/downgrade check) are still worth fixing individually if anyone wants to pick off a piece, but the full signing scheme isn't something we're committing to right now.Generated by Claude Code
demortes commentedon Sep 27, 2026
Opened #159 for the specific gap the audit flagged:
AutoUpdateService's checksum verification already existed but had no.sha256sidecar to check against becauserelease.ymlnever generated one. That PR just adds the CI step to publish sidecars — it does not implement the full signing scheme this issue describes, which stays deprioritized per the discussion above.Generated by Claude Code
demortes commentedon Oct 9, 2026
Status after the post-merge review (PRs #159 and #174): most of the gaps this issue documents are fixed; the signing scheme itself is still not done.
Fixed:
.sha256sidecar blocks the download and a mismatch deletes the file. (The old comparison was case-mismatched and never matched;Verifiedwas ignored byInstallUpdate.)%LOCALAPPDATA%\EDNexus\updates), not a predictable temp path.UpdateChecker/SemVerin Core is the single, unit-tested implementation. No ambientGITHUB_TOKENis sent; size caps; exact asset-name matching; Linux/macOS are not self-updated.release.ymlpublishes.sha256sidecars, validates the version input, uses least-privilege permissions and waits on tests.Still open (why this stays open, as a backlog item): a checksum from the same release proves integrity, not authenticity. There is an optional, inactive Authenticode step in
release.yml(needsWINDOWS_SIGNING_CERT_PFX_BASE64/WINDOWS_SIGNING_CERT_PASSWORD), but the ECDSA keypair scheme proposed here is not implemented and the updater does not yet require a signature.Status from a codebase review on 2026-10-09 (post-merge review PRs #169-#177, which are now merged).
🤖 Generated with Claude Code