This document summarizes how desktop_updater handles security reports and the
security boundaries of its desktop update flow.
The actively maintained line is 3.x. Security fixes are released on the latest
3.x version unless a project-specific migration note says otherwise.
The 1.x and 2.x lines are legacy. Applications still on either line should
migrate to 3.0 before relying on production update distribution.
Please do not publish exploit details in a public issue first.
Preferred report path:
- Open a private GitHub security advisory for this repository if GitHub offers that option to you.
- If private advisory reporting is not available, open a minimal public issue asking for a private disclosure channel. Do not include exploit payloads, secrets, or step-by-step abuse details in that public issue.
Useful report details:
- affected
desktop_updaterversion; - operating system and architecture;
- update source shape, such as direct HTTPS, S3-compatible storage, SFTP, FTP, or a custom upload command;
- whether the app pins the required signed metadata public keys;
- a minimal reproduction or proof sketch;
- expected impact, such as rollback, metadata spoofing, path traversal, stale file retention, or publisher-trust bypass.
Security fixes should include regression coverage when practical and should be
verified with the narrowest relevant flutter test --no-pub target before
release.
desktop_updater 3.0 uses a signed zip-first contract:
app-archive.json -> release.json -> app.zip
The updater treats these files as different trust layers:
app-archive.jsonis the small mutable index clients check first.- Each selected index item points to one versioned
release.json. release.jsonpoints to one zip artifact and records its expected length and SHA-256 digest.- The downloaded artifact is verified before staging or installing it.
- Ed25519 signatures on both
app-archive.jsonandrelease.jsonare required for production trust, and the app pins the corresponding public keys before selecting or downloading an update. Unsigned artifacts are candidate-only and must not be published as a production feed.
The selected app-archive.json item and downloaded release.json must agree on
release identity: version, build number when present, platform, and channel.
Hosted validation also checks descriptor identity before accepting a published
update.
desktop_updater verifies update mechanics and artifact integrity. The app
publisher still owns platform trust.
- macOS production updates should be Developer ID signed, hardened-runtime enabled, notarized, stapled, and Gatekeeper accepted before packaging.
- Windows production updates should Authenticode-sign and timestamp signable
.exeand.dllfiles when publisher trust is required. - Linux direct zip distribution should require signed descriptors or another publisher-authenticity policy before being treated as production trusted. Native package repositories and stores can provide their own signing and update policies.
Recent 3.0 hardening includes:
- release selection binding between
app-archive.jsonandrelease.json; - hosted
release validaterejection for descriptor identity mismatches; - SHA-256 and length verification before staging;
- safe zip extraction checks;
- mandatory signed index and descriptor verification with app-pinned Ed25519 public keys;
- owner/session/generation and full descriptor binding across stage and native install handoff;
- durable recovery markers retained after ambiguous native dispatch outcomes;
- top-level staged macOS
.appsymlink rejection, with a native helper recheck; - Windows and Linux whole-directory pruning before replacement so stale target files do not survive an update;
- opt-in notarized macOS
release publishflow that signs nested Flutter frameworks before the outer app bundle and verifies the notarized result before packaging; final ZIP/DMG/PKG audits recheck the exact distributable before release metadata signing; - feed-bound release-key profiles with automatic Ed25519 key IDs, protected
local private-key storage, authenticated encrypted export/import, existing
3.0 key adoption, and two-phase rotation. macOS/Linux use restrictive local
file permissions; Windows uses DPAPI
CurrentUserwithout a plaintext fallback. Key profiles are not runtime trust authorities.
Release publishing treats the macOS environment as a narrow configuration
input: only the canonical DESKTOP_UPDATER_MACOS_* reference variables are
accepted, YAML presence/type errors fail closed, and credentials are not
written to generated YAML or smoke logs. Windows DPAPI subprocesses use stdin,
concurrent bounded stream handling, timeout/kill/await, and redacted generic
errors. Hosted artifact validation retries only bounded transport failures;
metadata and trust failures remain fail-fast.
For production releases, prefer:
- HTTPS for
app-archive.json,release.json, and zip artifacts; - short cache TTLs for
app-archive.json; - long, immutable cache TTLs for versioned
release.jsonfiles and artifacts; - signed descriptors with release private keys kept outside the repository;
- CI gates for platform signing, package generation, hosted validation, and
post-upload
release validate --key-profile desktop_updater.keys.json; - uploading versioned files first and exposing
app-archive.jsonlast.
Internal scan artifacts, temporary reports, credentials, signing keys, and provider tokens should not be committed to this repository.