mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00

Add a design document for the update notifications mechanism, adapted from the write-up in the original GitHub issue. Refs #189
222 lines
10 KiB
Markdown
222 lines
10 KiB
Markdown
# Update notifications
|
|
|
|
This design document explains how the notification mechanism for Dangerzone
|
|
updates works, what are its benefits and limitations, and what other
|
|
alternatives we have considered. It has been adapted by discussions on GitHub
|
|
issue [#189](https://github.com/freedomofpress/dangerzone/issues/189), and has
|
|
been updated to reflect the current design.
|
|
|
|
A user-facing document on how update notifications work can be found in
|
|
https://github.com/freedomofpress/dangerzone/wiki/Updates
|
|
|
|
## Design overview
|
|
|
|
This feature introduces a hamburger icon that will be visible across almost all
|
|
of the Dangerzone windows. This will be used to notify the users about updates.
|
|
|
|
### First run
|
|
|
|
_We detect it's the first time Dangerzone runs because the
|
|
`settings["updater_last_check"] is None`._
|
|
|
|
Add the following keys in our `settings.json` file.
|
|
|
|
* `"updater_check": None`: Whether to check for updates or not. `None` means
|
|
that the user has not decided yet, and is the default.
|
|
* `"updater_last_check": None`: The last time we checked for updates (in seconds
|
|
from Unix epoch). None means that we haven't checked yet.
|
|
* `"updater_latest_version": "0.4.2"`: The latest version that the Dangerzone
|
|
updater has detected. By default it's the current version.
|
|
* `"updater_latest_changelog": ""`: The latest changelog that the Dangerzone
|
|
updater has detected. By default it's empty.
|
|
* `"updater_errors: 0`: The number of update check errors that we have
|
|
encountered in a row.
|
|
|
|
Note:
|
|
|
|
* If on Linux, make `"updater_check": False`, since we normally have
|
|
other update channels for these platforms.
|
|
|
|
### Second run
|
|
|
|
_We detect it's the second time Dangerzone runs because
|
|
`settings["updater_check"] is not None and settings["updater_last_check"] is
|
|
None`._
|
|
|
|
Before starting up the main window, show this window:
|
|
|
|
* Title: Dangerzone Updater
|
|
* Body:
|
|
|
|
> Do you want Dangerzone to automatically check for updates?
|
|
>
|
|
> If you accept, Dangerzone will check the latest releases page in github.com
|
|
> on startup. Otherwise it will make no network requests and won't inform you
|
|
> about new releases.
|
|
>
|
|
> If you prefer another way of getting notified about new releases, we suggest adding
|
|
> to your RSS reader our [Mastodon feed](https://fosstodon.org/@dangerzone.rss). For more information
|
|
> about updates, check [this webpage](https://github.com/freedomofpress/dangerzone/wiki/Updates).
|
|
|
|
* Buttons:
|
|
- Check Automaticaly: Store `settings["updater_check"] = True`
|
|
- Don't Check: Store `settings["updater_check"] = False`
|
|
|
|
Note:
|
|
* Users will be able to change their choice from the hamburger menu, which will
|
|
contain an entry called "Check for updates", that users can check and uncheck.
|
|
|
|
### Subsequent runs
|
|
|
|
_We perform the following only if `settings["updater_check"] == True`._
|
|
|
|
1. Spawn a new thread so that we don't block the main window.
|
|
2. Check if we have cached information about a release (version and changelog).
|
|
If yes, return those immediately.
|
|
3. Check if the last time we checked for new releases was less than 12 hours
|
|
ago. In that case, skip this update check so that we don't leak telemetry
|
|
stats to GitHub.
|
|
4. Hit the GitHub releases API and get the [latest release](https://api.github.com/repos/freedomofpress/dangerzone/releases/latest).
|
|
Store the current time as the last check time, even if the call fails.
|
|
5. Check if the latest release matches `settings["updater_latest_version"]`. If
|
|
yes, return an empty update report.
|
|
6. If a new update has been detected, return the version number and the
|
|
changelog.
|
|
7. Add a green bubble in the notification icon, and a menu entry called "New
|
|
version available".
|
|
8. Users who click on this entry will see a dialog with more info:
|
|
|
|
* Title: "Dangerzone v0.5.0 has been released"
|
|
* Body:
|
|
|
|
> A new Dangerzone version been released. Please visit our [downloads page](https://dangerzone.rocks#downloads) to install this update.
|
|
>
|
|
> (Show changelog rendered from Markdown in a collapsible text box)
|
|
* Buttons:
|
|
- OK: Return
|
|
|
|
Notes:
|
|
* Any successful attempt to fetch info from GitHub will result in clearing the
|
|
`settings["updater_errors"]` key.
|
|
|
|
### Error handling
|
|
|
|
_We trigger error handling when the updater thread encounters an error (either
|
|
due to an HTTPS failure or a Python exception) and does not complete
|
|
successfully._
|
|
|
|
1. Bump the number of errors we've encountered in a row
|
|
(`settings["updater_errors"] += 1`)
|
|
2. Return an update report with the error we've encountered.
|
|
3. Update the hamburger menu with a red notification bubble, and add a menu
|
|
entry called "Update error".
|
|
4. If a user clicks on this menu entry, show a dialog window:
|
|
* Title: "Update check error"
|
|
* Body:
|
|
|
|
> Something went wrong while checking for Dangerzone updates:
|
|
>
|
|
> You are strongly advised to visit our [downloads page](https://dangerzone.rocks#downloads) and check for new updates manually, or consult [this page](https://github.com/freedomofpress/dangerzone/wiki/Updates) for common causes of errors . Alternatively, you can uncheck "Check for updates", if you are in an air-gapped environment and have another way of learning about updates.
|
|
>
|
|
> (Show the latest error message in a scrollable, copyable text box)
|
|
|
|
* Buttons:
|
|
- Close: Return
|
|
|
|
## Key Benefits
|
|
|
|
1. The above approach future-proofs Dangerzone against API changes or bugs in
|
|
the update check process, by asking users to manually visit
|
|
https://dangerzone.rocks.
|
|
2. If we want to draw the attention of users to immediately install a release,
|
|
we can do so in the release body, which we will show in a pop-up window.
|
|
3. If we are aware of issues that prevent updates, we can add them in the wiki
|
|
page that we show in the error popup. Wiki pages are not versioned, so we can
|
|
add useful info even after a release.
|
|
|
|
## Security Considerations
|
|
|
|
Because this approach does not download binaries / auto-updates, it **does not
|
|
add any more security issues** than the existing, manual way of installing
|
|
updates. These issues have to do with a compromised/malicous GitHub service, and
|
|
are the following:
|
|
|
|
1. GitHub pages can alter the contents of our main site
|
|
(https://dangerzone.rocks)
|
|
2. GitHub releases can serve an older, vulnerable version of Dangerzone, instead
|
|
of a new update.
|
|
3. GitHub releases can serve a malicious binary (requires a joint operation from
|
|
a malicious CA as well, for extra legitimacy).
|
|
4. GitHub releases can silently drop updates.
|
|
5. GitHub releases can know which users download Dangerzone updates.
|
|
6. Network attackers can know that a user has Dangerzone installed (because we ask the user to visit https://dangerzone.rocks)
|
|
|
|
A good update framework would probably defend against 1,2,3. This is not to say
|
|
that our users are currently unprotected, since 1-4 can be detected by the
|
|
general public and the developers (unless GitHub specifically targets an
|
|
individual, but that's another story).
|
|
|
|
## Usability Considerations
|
|
|
|
1. We do not have an update story for users that only use the Dangerzone CLI. A
|
|
good assumption is that they are on Linux, so they auto-update.
|
|
|
|
## Alternatives
|
|
|
|
We researched a bit on this subject and found out that there are update
|
|
frameworks that do this job for us. While working on this issue, we decided that
|
|
integrating with one framework will certainly take a bit of work, especially
|
|
given that we target both Windows and MacOS systems. In the meantime though, we
|
|
didn't want to have releases out without including at least a notification
|
|
channel, since staying behind on updates has a huge negative impact on the
|
|
users' safety.
|
|
|
|
The update frameworks that we learned about are:
|
|
|
|
## Sparkle Project
|
|
|
|
[Sparkle project](https://sparkle-project.org) seems to be the de-facto update
|
|
framework in MacOS. Integrators in practice need to care about two things:
|
|
creating a proper `Appcast.xml` file on the server-side, and calling the Sparkle
|
|
code from the client-side. These are covered in the project's
|
|
[documentation](https://sparkle-project.org/documentation/).
|
|
|
|
The client-side part is not very straight-forward, since Sparkle is written in
|
|
Objective-C. Thankfully, there are others who have ventured into this before:
|
|
https://fman.io/blog/codesigning-and-automatic-updates-for-pyqt-apps/
|
|
|
|
The server-side part is also not very straight-forward. For integrators that use
|
|
GitHub releases (like us), this issue may be of help:
|
|
https://github.com/sparkle-project/Sparkle/issues/648
|
|
|
|
The Windows platform is not covered by Sparkle itself, but there are other
|
|
projects, such as [WinSparkle](https://winsparkle.org/), that follow a similar
|
|
approach. I see that there's a [Python library (`pywinsparkle`)](https://pypi.org/project/pywinsparkle/)
|
|
for interacting with WinSparkle, so this may alleviate some pains.
|
|
|
|
Note that the Sparkle project is not a silver bullet. Development missteps can
|
|
happen, and users can be left without updates. Here's an [example issue](https://github.com/sparkle-project/Sparkle/issues/345) that showcases this.
|
|
|
|
## The Update Framework
|
|
|
|
[The Update Framework](https://theupdateframework.io/) is a graduated CNCF
|
|
project hosted by Linux Foundation. It's based on the
|
|
[Thandy](https://chromium.googlesource.com/chromium/src.git/+/master/docs/updater/protocol_3_1.md)
|
|
updater for Tor. It's [not widely adopted](https://github.com/sparkle-project/Sparkle/issues/345), but some of its
|
|
adopters are high-profile, and it has passed security audits.
|
|
|
|
It's more of a [specification](https://github.com/sparkle-project/Sparkle/issues/345)
|
|
and less of a software project, although a well-maintained
|
|
[reference implementation](https://github.com/sparkle-project/Sparkle/issues/345)
|
|
in Python exists. Also, a [Python project (`tufup`)](https://doc.qt.io/qtinstallerframework/ifw-updates.html)
|
|
that builds upon this implementation makes it even easier to generate the
|
|
required keys and files.
|
|
|
|
Regardless of whether we use it, knowing about the [threat vectors](https://theupdateframework.io/security/) that it's protecting against is very important.
|
|
|
|
## Other Projects
|
|
|
|
* Qt has some updater framework as well: https://doc.qt.io/qtinstallerframework/ifw-updates.html
|
|
* Google Chrome has it's own updater framework: https://chromium.googlesource.com/chromium/src.git/+/master/docs/updater/protocol_3_1.md
|
|
* Keepass rolls out its own way to update: https://github.com/keepassxreboot/keepassxc/blob/develop/src/updatecheck/UpdateChecker.cpp
|
|
* [PyUpdater](https://github.com/Digital-Sapphire/PyUpdater) was another popular updater project for Python, but is now archived.
|