Compare commits

..

142 Commits

Author SHA1 Message Date
9230596037 chore: merge Laviolette to master 2026-01-28 16:14:32 +00:00
87bba395d0 chore: Laviolette Bridge 3.22.0 changelog. 2026-01-22 16:47:19 +01:00
4ab5bcdf65 fix(BRIDGE-449): IMAP OK heartbeat; gluon bump 2026-01-21 15:54:03 +00:00
6194a1a125 chore: bump badssl pin 2026-01-21 12:43:51 +01:00
c6ec3cb53f fix(BRIDGE-447): bump GPA 2026-01-09 12:27:25 +01:00
16e1177ec2 chore(BRIDGE-455): bump go toolchain to 1.24.11; logrus package and cloudflare/circl bump; bump GPA and Gluon; remove ignored vulns 2026-01-08 13:00:18 +01:00
194942e895 chore: Happy New Year (2026) 2026-01-08 13:00:18 +01:00
fee4570a3a ci: DEVEX-54 migrating win runners. 2025-12-15 12:48:48 +01:00
63ea76d757 chore(BRIDGE-440): supress govulnechk findings x2 2025-12-09 12:29:09 +01:00
3db547d187 feat(BRIDGE-409): update gluon and go-proton-api to the latest version. 2025-11-27 12:04:21 +01:00
4378adab1a chore: bump badssl pin 2025-11-26 15:50:57 +01:00
726eb72026 chore: Bump VCPKG on Windows; resolve build issues 2025-11-10 12:35:12 +01:00
5a28fbf2df feat(BRIDGE-409): update gluon to fix max message size issue. 2025-11-05 11:09:19 +01:00
131a663578 chore(BRIDGE-440): supress govulnechk findings 2025-10-30 14:48:40 +01:00
6ab8558e17 chore: Attempt at stabilizing Bridge launch for e2e tests 2025-10-24 14:57:25 +02:00
ac3d7375f7 fix(BRIDGE-424): patch second password prompt 2025-10-01 11:42:48 +02:00
cd375c81fa chore(BRIDGE-429): migrate GitHub forks to ProtonMail organization 2025-09-30 12:06:44 +02:00
7fce966a65 fix(BRIDGE-424): resolved wrong layout ordering; u2f pin option is now treated as a 'password' 2025-09-29 11:58:09 +02:00
edf903fd21 feat(BRIDGE-424): FIDO2 GUI support. 2025-09-25 17:09:00 +02:00
e091e58be1 feat(BRIDGE-424): implement FIDO2 support 2025-09-24 14:42:15 +00:00
2fb5b751b6 chore(BRIDGE-428): suppress vulnerability finding - GO-2025-3956 2025-09-24 16:09:05 +02:00
e442b67ac0 chore: bump badssl pin 2025-09-24 15:57:35 +02:00
8baa7e3b6e chore: Got rid of something that is not needed, but was failing the pipeline. 2025-08-15 13:31:29 +00:00
5bb71575c3 chore: Changes made so that the e2e tests can be run on a scheduled nightly pipeline. 2025-08-15 10:24:03 +02:00
fd709b0d08 test(BRIDGE-136): Download Bridge 2025-08-15 07:25:10 +00:00
cf9b35163a chore: let's see 2025-08-13 12:44:21 +00:00
f3b3fd960b chore: BRIDGE-416 silence database/sql vuln 2025-08-12 16:30:50 +02:00
02684e27fb chore: Kanmon Bridge 3.21.2 changelog. 2025-07-21 16:46:06 +02:00
f6d4fc0c29 chore: Kanmon Bridge 3.21.1 changelog. 2025-07-21 16:46:00 +02:00
054a51ba15 chore: Kanmon Bridge 3.21.0 changelog. 2025-07-21 16:45:53 +02:00
7b2ea635ee chore(BRIDGE-409): Bump GPA 2025-07-18 15:39:36 +02:00
7faf32d0ff feat(BRIDGE-396): Observability metrics for vault issues; Extension to observability service to support caching 2025-07-17 14:06:23 +02:00
c32c431640 chore: merge Kanmon to master 2025-07-10 13:36:03 +00:00
de3fd34998 feat(BRIDGE-356): Added retry logic for unavailable preferred keychain on Linux; Feature flag support before bridge initialization; Refactored some bits of the code; 2025-07-10 13:23:26 +00:00
20183bf984 fix(BRIDGE-374): Tweaked MBOX header sanitization logic, ensures that RFC822 headers are present. 2025-07-10 12:08:47 +02:00
47316a1843 ci: DEVOPS-3481 adding back cache (reduces build time by 1h) 2025-07-08 10:31:55 +02:00
4cc2ded001 chore: Kanmon Bridge 3.21.2 changelog. 2025-07-07 11:34:42 +02:00
366f7eb4e0 feat(BRIDGE-358): hover tooltip on copy to clipboard UI button 2025-07-07 08:49:02 +00:00
15880dfe19 fix(BRIDGE-406): fixed faulty certificate chain validation logic; made certificate pin checks exclusive to leaf certs; 2025-07-04 15:19:44 +02:00
5ea45790fe fix(BRIDGE-406): fixed faulty certificate chain validation logic; made certificate pin checks exclusive to leaf certs; 2025-07-04 15:19:05 +02:00
7e8d16e883 feat(BRIDGE-151): Additional sentry report for auto-update error case 2025-07-02 09:35:21 +00:00
e9b1befae4 chore: refactor; unify vault mutex usage with wrappers; 2025-07-02 08:59:44 +00:00
26cc6169fa feat(BRIDGE-361): log the utilized keychain helper 2025-07-01 10:56:50 +02:00
be9e03d917 feat(BRIDGE-278): Rollout Feature Flag stickiness support; a new UUID 'sticky' value has been added to the vault" 2025-06-27 16:21:40 +02:00
2669bb4df9 fix(BRIDGE-395): skip saving the last used keychain helper as user preference on Windows & macOS 2025-06-27 13:10:41 +00:00
bffad05933 fix(BRIDGE-394): bump Gluon 2025-06-27 12:29:47 +02:00
7269e5ece0 fix(BRIDGE-355): bump GPA 2025-06-27 12:27:50 +02:00
6ee9e6e38b chore(BRIDGE-369): bump GPA & gopenpg to 2.9.0 2025-06-24 12:41:02 +02:00
2f2a8a200b chore(BRIDGE-392): bump go to 1.24.4 2025-06-24 10:29:20 +02:00
5832d48a5b chore: remove windows env vcpkg cache 2025-06-23 15:07:42 +00:00
6b1d203225 feat(BRIDGE-391): Simplified internal label conflict resolver 2025-06-17 13:09:39 +02:00
dbef40cfc5 chore: merge Kanmon to master 2025-06-12 16:54:19 +00:00
b699a53b12 chore(BRIDGE-388): disable particular vuln reporting 2025-06-12 12:17:09 +02:00
4ee149ecd7 fix(BRIDGE-387): use gluon ID for getting the message count instead of the address ID 2025-06-12 11:47:33 +02:00
e9ea976773 chore: Kanmon Bridge 3.21.1 changelog. 2025-06-11 16:15:53 +02:00
a00af3a398 feat(BRIDGE-383): Internal mailbox conflict resolution extended; Minor alterations to mailbox conflict pre-checker 2025-06-11 16:11:20 +02:00
7b533d5951 feat(BRIDGE-383): Internal mailbox conflict resolution extended; Minor alterations to mailbox conflict pre-checker 2025-06-11 14:09:53 +02:00
8b891fb3e7 chore: merge Kanmon to master 2025-06-09 13:05:03 +00:00
94125056ab chore: merge Jubilee to master 2025-05-29 14:02:18 +00:00
675b37a2fa chore: Jubilee Bridge 3.20.1 changelog. 2025-05-28 10:20:12 +02:00
9d4415d8cc fix(BRIDGE-362): added label conflict reconciliation logic 2025-05-28 09:27:57 +02:00
4557f54e2f chore: merge Jubilee to master 2025-05-06 14:51:31 +00:00
05623a9e49 chore: Jubilee Bridge 3.20.0 changelog. 2025-04-24 13:40:43 +02:00
42605c1923 chore: merge Infinity to master 2025-03-18 15:03:14 +00:00
9f4801b738 chore: Infinity Bridge 3.19.0 changelog. 2025-03-07 11:12:59 +01:00
4e6236611a chore: merge Helix to master 2025-02-27 10:36:11 +00:00
0800aeea50 chore: Helix Bridge 3.18.0 changelog. 2025-02-18 23:44:44 +01:00
b230f2ece6 chore: merge XXX to master 2025-02-12 08:37:01 +00:00
d44c488ed5 chore: minor comment just so we have a new commit 2025-02-11 10:28:05 +01:00
8237129670 chore: merge Grunwald to master 2025-01-29 15:52:34 +00:00
8e634995c5 chore: Grunwald Bridge 3.17.0 changelog. 2025-01-21 14:42:52 +01:00
10a685a123 chore: Prepare for issue tracker removal 2025-01-14 10:48:03 +01:00
896f50c754 chore: FF devel into master 2025-01-14 10:35:25 +01:00
60633fc09c chore: merge Flavien to master 2024-12-17 15:20:30 +00:00
9c5b5c2ac3 chore: FF devel into master 2024-12-16 12:22:45 +01:00
4f4a2c3fd8 chore: merge Erasmus to master 2024-12-05 11:35:19 +00:00
120a7b3626 chore: Erasmus Bridge 3.15.1 changelog. 2024-12-04 14:44:25 +01:00
7cf3b6fb7b feat(BRIDGE-281): disable keychain test on macOS.
(cherry picked from commit 3f78f4d672)
2024-12-04 14:09:50 +01:00
03c9455b0d chore: Flavien Bridge 3.16.0 changelog. 2024-12-04 10:03:12 +01:00
61ca604ace chore: merge Erasmus to master 2024-11-13 09:30:24 +00:00
a8caec560e chore: Erasmus Bridge 3.15.0 changelog. 2024-10-29 10:47:33 +01:00
df78e29234 chore: merge Dragon to master 2024-09-30 09:05:11 +00:00
6105f32c75 chore: Dragon Bridge 3.14.0 changelog. 2024-09-25 10:47:40 +02:00
da76784290 chore: merge Colorado to master 2024-09-10 12:05:30 +00:00
43cbedafb8 chore: Colorado Bridge 3.13.0 changelog. 2024-08-30 15:35:30 +02:00
0d33cc5000 chore: merge Bastei to master 2024-06-19 06:06:24 +00:00
ed5adb18fb chore: Bastei Bridge 3.12.0 changelog. 2024-06-17 11:19:49 +02:00
85a91c5572 feat(BRIDGE-97): added repair button telemetry 2024-06-14 13:01:07 +00:00
56d4bfbb71 feat(BRIDGE-79): update to the KB suggestion list. 2024-06-13 10:05:23 +02:00
48a75b0dd7 chore: Bastei Bridge 3.12.0 changelog. 2024-06-06 10:10:36 +02:00
b84663dd7a chore: merge Alcantara to master 2024-05-21 09:32:21 +00:00
cd8db6fd1c chore: Alcantara Bridge 3.11.1 changelog. 2024-05-16 15:12:56 +02:00
a5e0f85a58 fix(BRIDGE-70): hotfix for blocked smtp/imap port causing bridge to quit 2024-05-16 09:51:32 +02:00
6cbe51138a chore: merge Alcantara to master 2024-04-29 12:31:37 +00:00
82607efe1c chore: Alcantara Bridge 3.11.0 changelog. 2024-04-23 17:07:24 +02:00
961dc9435f fix(BRIDGE-15): Apple Mail profile install page was not properly reset before showing. 2024-04-23 15:58:22 +02:00
b574ccb6ea chore: Alcantara Bridge 3.11.0 changelog. 2024-04-22 10:37:47 +02:00
2569e83e51 chore: Alcantara Bridge 3.11.0 changelog. 2024-04-22 09:27:43 +02:00
f34a7ff0ed chore: merge Zaehringen to master 2024-03-12 12:27:21 +00:00
da069a0155 chore: Zaehringen Bridge 3.10.0 changelog. 2024-03-06 10:33:17 +01:00
384fa4eb4b chore: merge Ypsilon to master 2024-02-12 11:19:51 +00:00
0c6e4ffa35 chore: merge Xikou to master 2024-02-03 00:14:41 +01:00
4951244400 chore: Xikou Bridge 3.8.2 changelog. 2024-02-02 19:32:58 +01:00
d65d6ee2e5 fix(GODT-3235): use release xikou for trigger build 2024-02-02 18:37:38 +01:00
097d6f86d3 fix(GODT-3235): update bridge update key 2024-02-02 17:34:32 +01:00
9894cf9744 chore: merge Ypsilon to master 2024-01-31 11:00:11 +00:00
f84067de3e chore: merge Xikou to master 2023-12-12 13:39:06 +01:00
f885bfbcf4 chore: merge Xikou to master 2023-12-11 17:04:00 +01:00
f3aac09ecb chore: merge wakato release to master 2023-11-22 12:52:24 +01:00
38d692ebfb chore: merge wakato release to master 2023-11-14 11:32:39 +01:00
1acc7eb7db chore: merge release/vasco_da_gama to master 2023-11-03 17:10:42 +01:00
248fbf5e33 chore: Vasco da Gama Bridge 3.6.1 changelog. 2023-10-18 15:41:01 +02:00
8b12a454ea fix(GODT-3033): Unable to receive new mail
If the IMAP service happened to finish syncing and wanted to reset the
user event service at a time the latter was publishing an event a
deadlock would occur and the user would not receive any new messages.

This change puts the request to revert the event id in a separate
go-routine to avoid this situation from re-occurring. The operational
flow remains unchanged as the event service will only process this
request once the current set of events have been published.
2023-10-18 14:46:14 +02:00
310fcffc7b chore: merge release/vasco_da_gama to master 2023-10-17 11:54:05 +02:00
318ad16378 chore: merge Umshiang release to master 2023-10-13 08:40:01 +02:00
8be4246f7e chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-11 16:09:55 +02:00
e580f89106 feat(GODT-3004): update gopenpgp and dependencies. 2023-10-11 15:29:52 +02:00
01043e033e chore: Umshiang Bridge 3.5.3 changelog. 2023-10-11 08:37:28 +02:00
94b44b383a feat(GODT-3004): update gopenpgp and dependencies. 2023-10-11 08:26:58 +02:00
a3b8fabb26 chore: merge Umshiang to master 2023-10-10 13:46:07 +02:00
275b30e518 chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-10 11:29:36 +02:00
bf244e5c86 fix(GODT-3003): Ensure IMAP State is reset after vault corruption
After we detect that the user has suffered the GODT-3003 bug due the
vault corruption not ensuring that a previous sync state would be
erased, we patch the gluon db directly and then reset the sync state.

After the account is added, the sync is automatically triggered and the
account state fixes itself.
2023-10-10 11:24:06 +02:00
cf9651bb94 fix(GODT-3001): Only create system labels during system label sync 2023-10-10 11:23:32 +02:00
ba65ffdbc7 chore: Umshiang Bridge 3.5.2 changelog. 2023-10-10 11:22:41 +02:00
4b95ef4d82 chore: Umshiang Bridge 3.5.2 changelog. 2023-10-09 13:25:44 +02:00
951c7c27fb fix(GODT-3003): Ensure IMAP State is reset after vault corruption
After we detect that the user has suffered the GODT-3003 bug due the
vault corruption not ensuring that a previous sync state would be
erased, we patch the gluon db directly and then reset the sync state.

After the account is added, the sync is automatically triggered and the
account state fixes itself.
2023-10-09 11:19:36 +01:00
e7423a9519 fix(GODT-3001): Only create system labels during system label sync 2023-10-09 11:05:59 +01:00
d3582fa981 chore: Vasco da Gama Bridge 3.6.0 changelog. 2023-10-03 16:43:33 +02:00
80c852a5b2 fix(GODT-2992): fix link in 'no account view' in main window after 2FA or TOTP are cancelled.
(cherry picked from commit 1c344211d1)
2023-10-03 11:08:52 +02:00
51498e3e37 chore: merge master with release/umshiang 2023-09-28 14:19:45 +02:00
b7ef6e1486 chore: Umshiang Bridge 3.5.1 changelog. 2023-09-27 13:18:23 +02:00
0d03f84711 fix(GODT-2963): Use multi error to report file removal errors
Do not abort removing files on first error. Collect errors and try to
remove as many as possible. This would cause some state files to not be
removed on windows.
2023-09-27 12:34:07 +02:00
949666724d chore: Umshiang Bridge 3.5.1 changelog. 2023-09-27 10:54:50 +02:00
bbe19bf960 fix(GODT-2956): Restore old deletion rules
When unlabeling a message from trash we have to check if this message is
present in another folder before perma-deleting.
2023-09-26 14:06:31 +02:00
bfe25e3a46 fix(GODT-2951): Negative WaitGroup Counter
Do not defer call to `wg.Done()` in `job.onJobFinished`. If there is an
error it will also call `wg.Done()`.
2023-09-26 13:58:46 +02:00
236c958703 fix(GODT-2590): Fix send on closed channel
Ensure periodic user tasks are terminated before the other user
services. The panic triggered due to the fact that the telemetry service
was shutdown before this periodic task.
2023-09-26 13:58:18 +02:00
e6b312b437 fix(GODT-2949): Fix close of close channel in event service
This issue is triggered due to the `Service.Close()` call after the
go-routine for the event service exists. It is possible that during this
period a recently added subscriber with `pendingOpAdd` gets cancelled
and closed.

However, the subscriber later also enqueues a `pendingOpRemove` which
gets processed again with a call in `user.eventService.Close()` leading
to the double close panic.

This patch simply removes the `s.Close()` from the service, and leaves
the cleanup to called externally from user.Close() or user.Logout().
2023-09-26 13:58:07 +02:00
384154c767 chore: merge 'trift' into umshiang 2023-09-14 14:48:03 +02:00
45d2e9ea63 chore: update changelog. 2023-09-13 10:25:47 +02:00
86e8a566c7 chore: Umshiang Bridge 3.5.0 changelog. 2023-09-12 07:45:08 +02:00
a80fd92018 chore: Trift Bridge 3.4.2 changelog. 2023-09-01 15:12:34 +02:00
71063ac5ee fix(GODT-2902): do not check for changed values. Related to GODT-2857. 2023-09-01 14:44:27 +02:00
694 changed files with 7034 additions and 4352 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "submodules/vcpkg"]
path = extern/vcpkg
url = https://github.com/Microsoft/vcpkg.git
[submodule "extern/vcpkg-windows"]
path = extern/vcpkg-windows
url = https://github.com/microsoft/vcpkg.git

View File

@ -42,7 +42,10 @@ Proton Mail Bridge includes the following 3rd party software:
* [go-smtp](https://github.com/emersion/go-smtp) available under [license](https://github.com/emersion/go-smtp/blob/master/LICENSE)
* [go-vcard](https://github.com/emersion/go-vcard) available under [license](https://github.com/emersion/go-vcard/blob/master/LICENSE)
* [color](https://github.com/fatih/color) available under [license](https://github.com/fatih/color/blob/master/LICENSE)
* [cbor](https://github.com/fxamacker/cbor/v2) available under [license](https://github.com/fxamacker/cbor/v2/blob/master/LICENSE)
* [sentry-go](https://github.com/getsentry/sentry-go) available under [license](https://github.com/getsentry/sentry-go/blob/master/LICENSE)
* [ctaphid](https://github.com/go-ctap/ctaphid) available under [license](https://github.com/go-ctap/ctaphid/blob/master/LICENSE)
* [winhello](https://github.com/go-ctap/winhello) available under [license](https://github.com/go-ctap/winhello/blob/master/LICENSE)
* [resty](https://github.com/go-resty/resty/v2) available under [license](https://github.com/go-resty/resty/v2/blob/master/LICENSE)
* [dbus](https://github.com/godbus/dbus) available under [license](https://github.com/godbus/dbus/blob/master/LICENSE)
* [mock](https://github.com/golang/mock) available under [license](https://github.com/golang/mock/blob/master/LICENSE)
@ -52,6 +55,7 @@ Proton Mail Bridge includes the following 3rd party software:
* [html2text](https://github.com/jaytaylor/html2text) available under [license](https://github.com/jaytaylor/html2text/blob/master/LICENSE)
* [go-locale](https://github.com/jeandeaual/go-locale) available under [license](https://github.com/jeandeaual/go-locale/blob/master/LICENSE)
* [go-keychain](https://github.com/keybase/go-keychain) available under [license](https://github.com/keybase/go-keychain/blob/master/LICENSE)
* [go-libfido2](https://github.com/keys-pub/go-libfido2) available under [license](https://github.com/keys-pub/go-libfido2/blob/master/LICENSE)
* [dns](https://github.com/miekg/dns) available under [license](https://github.com/miekg/dns/blob/master/LICENSE)
* [memory](https://github.com/pbnjay/memory) available under [license](https://github.com/pbnjay/memory/blob/master/LICENSE)
* [errors](https://github.com/pkg/errors) available under [license](https://github.com/pkg/errors/blob/master/LICENSE)
@ -70,7 +74,6 @@ Proton Mail Bridge includes the following 3rd party software:
* [grpc](https://google.golang.org/grpc) available under [license](https://github.com/grpc/grpc-go/blob/master/LICENSE)
* [protobuf](https://google.golang.org/protobuf) available under [license](https://github.com/protocolbuffers/protobuf/blob/main/LICENSE)
* [plist](https://howett.net/plist) available under [license](https://github.com/DHowett/go-plist/blob/main/LICENSE)
* [compute](https://cloud.google.com/go/compute) available under [license](https://pkg.go.dev/cloud.google.com/go/compute?tab=licenses)
* [metadata](https://cloud.google.com/go/compute/metadata) available under [license](https://pkg.go.dev/cloud.google.com/go/compute/metadata?tab=licenses)
* [bcrypt](https://github.com/ProtonMail/bcrypt) available under [license](https://github.com/ProtonMail/bcrypt/blob/master/LICENSE)
* [go-crypto](https://github.com/ProtonMail/go-crypto) available under [license](https://github.com/ProtonMail/go-crypto/blob/master/LICENSE)
@ -111,6 +114,7 @@ Proton Mail Bridge includes the following 3rd party software:
* [multierror](https://github.com/joeshaw/multierror) available under [license](https://github.com/joeshaw/multierror/blob/master/LICENSE)
* [go](https://github.com/json-iterator/go) available under [license](https://github.com/json-iterator/go/blob/master/LICENSE)
* [cpuid](https://github.com/klauspost/cpuid/v2) available under [license](https://github.com/klauspost/cpuid/v2/blob/master/LICENSE)
* [cose](https://github.com/ldclabs/cose) available under [license](https://github.com/ldclabs/cose/blob/master/LICENSE)
* [go-urn](https://github.com/leodido/go-urn) available under [license](https://github.com/leodido/go-urn/blob/master/LICENSE)
* [go-colorable](https://github.com/mattn/go-colorable) available under [license](https://github.com/mattn/go-colorable/blob/master/LICENSE)
* [go-isatty](https://github.com/mattn/go-isatty) available under [license](https://github.com/mattn/go-isatty/blob/master/LICENSE)
@ -131,6 +135,7 @@ Proton Mail Bridge includes the following 3rd party software:
* [golang-asm](https://github.com/twitchyliquid64/golang-asm) available under [license](https://github.com/twitchyliquid64/golang-asm/blob/master/LICENSE)
* [codec](https://github.com/ugorji/go/codec) available under [license](https://github.com/ugorji/go/codec/blob/master/LICENSE)
* [tagparser](https://github.com/vmihailenco/tagparser/v2) available under [license](https://github.com/vmihailenco/tagparser/v2/blob/master/LICENSE)
* [float16](https://github.com/x448/float16) available under [license](https://github.com/x448/float16/blob/master/LICENSE)
* [smetrics](https://github.com/xrash/smetrics) available under [license](https://github.com/xrash/smetrics/blob/master/LICENSE)
* [go-ordered-json](https://gitlab.com/c0b/go-ordered-json) available under [license](https://gitlab.com/c0b/go-ordered-json/blob/master/LICENSE)
* [go.opencensus.io](https://pkg.go.dev/go.opencensus.io?tab=licenses) available under [license](https://pkg.go.dev/go.opencensus.io?tab=licenses)
@ -142,9 +147,11 @@ Proton Mail Bridge includes the following 3rd party software:
* [appengine](https://google.golang.org/appengine) available under [license](https://pkg.go.dev/google.golang.org/appengine?tab=licenses)
* [genproto](https://google.golang.org/genproto) available under [license](https://pkg.go.dev/google.golang.org/genproto?tab=licenses)
* [yaml](https://gopkg.in/yaml.v3) available under [license](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE) available under [license](https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE)
* [go-autostart](https://github.com/ElectroNafta/go-autostart) available under [license](https://github.com/ElectroNafta/go-autostart/blob/master/LICENSE)
* [go-autostart](https://github.com/ProtonMail/go-autostart) available under [license](https://github.com/ProtonMail/go-autostart/blob/master/LICENSE)
* [go-message](https://github.com/ProtonMail/go-message) available under [license](https://github.com/ProtonMail/go-message/blob/master/LICENSE)
* [go-smtp](https://github.com/ProtonMail/go-smtp) available under [license](https://github.com/ProtonMail/go-smtp/blob/master/LICENSE)
* [resty](https://github.com/LBeernaertProton/resty/v2) available under [license](https://github.com/LBeernaertProton/resty/v2/blob/master/LICENSE)
* [go-keychain](https://github.com/cuthix/go-keychain) available under [license](https://github.com/cuthix/go-keychain/blob/master/LICENSE)
* [winhello](https://github.com/ProtonMail/winhello) available under [license](https://github.com/ProtonMail/winhello/blob/master/LICENSE)
* [resty](https://github.com/ProtonMail/resty/v2) available under [license](https://github.com/ProtonMail/resty/v2/blob/master/LICENSE)
* [go-keychain](https://github.com/ProtonMail/go-keychain) available under [license](https://github.com/ProtonMail/go-keychain/blob/master/LICENSE)
* [go-libfido2](https://github.com/ProtonMail/go-libfido2) available under [license](https://github.com/ProtonMail/go-libfido2/blob/master/LICENSE)
<!-- END AUTOGEN -->

View File

@ -3,6 +3,45 @@
Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Laviolette Bridge 3.22.0
### Added
* BRIDGE-358: Hover tooltip for IMAP/SMTP settings clipboard UI actions.
* BRIDGE-356: Added support for unavailable keychain retries on Linux, such that we don't wipe the vault. Feature flag support before Bridge initialization.
* BRIDGE-278: Rollout feature flag support.
* BRIDGE-151: Additional sentry reporting related to auto-update failures.
* BRIDGE-361: Debug information on the utilized keychain helper.
* BRIDGE-396: Observability caching and metrics for vault related issues.
* BRIDGE-449: Generic IMAP OK heartbeat for long lasting commands.
### Changed
* BRIDGE-374: Modified MBOX header sanitization logic, it now ensures that RFC822 headers are present before stripping content.
* BRIDGE-391: Simplified internal label conflict resolver.
* BRIDGE-409: Increased the import size limit to 55MB.
* BRIDGE-369: Bumped gopenpgp to v2.9.0.
* BRIDGE-455: Bumped Go to 1.24.11.
* BRIDGE-424: FIDO2 support.
### Fixed
* BRIDGE-395: Don't store the last utilized keychain as the user preference on Windows & macOS.
* BRIDGE-387: Use the address gluon ID, instead of the address ID to fetch message counts.
* BRIDGE-394: Prevent the RFC822 parser from mutating the message literal.
* BRIDGE-355: Prevent Bridge from crashing when an unknown message charset is detected on Import.
* BRIDGE-447: Adjusted error message for message import size limitation.
## Kanmon Bridge 3.21.2
### Fixed
* BRIDGE-406: Fixed faulty certificate chain validation logic. Made certificate pin checks exclusive to leaf certificates.
## Kanmon Bridge 3.21.1
### Changed
* BRIDGE-383: Extended internal mailbox conflict resolution logic and minor changes to the mailbox conflict pre-checker.
## Kanmon Bridge 3.21.0
### Added

View File

@ -9,10 +9,10 @@ TARGET_OS?=${GOOS}
ROOT_DIR:=$(realpath .)
## Build
.PHONY: build build-gui build-nogui build-launcher versioner hasher
.PHONY: build build-gui build-nogui build-launcher versioner hasher install-libfido2
# Keep version hardcoded so app build works also without Git repository.
BRIDGE_APP_VERSION?=3.21.0+git
BRIDGE_APP_VERSION?=3.22.0+git
APP_VERSION:=${BRIDGE_APP_VERSION}
APP_FULL_NAME:=Proton Mail Bridge
APP_VENDOR:=Proton AG
@ -32,6 +32,24 @@ BUILD_FLAGS_LAUNCHER:=${BUILD_FLAGS}
GO_LDFLAGS:=$(addprefix -X github.com/ProtonMail/proton-bridge/v3/internal/constants., Version=${APP_VERSION} Revision=${REVISION} Tag=${TAG} BuildTime=${BUILD_TIME})
GO_LDFLAGS+=-X "github.com/ProtonMail/proton-bridge/v3/internal/constants.FullAppName=${APP_FULL_NAME}"
## Libfido2 set-up.
# We use vcpkg for libfido2 on *nix systems.
VCPKG_ROOT_NIX := $(ROOT_DIR)/extern/vcpkg
ifeq "${TARGET_OS}" "darwin"
VCPKG_INSTALLED_ARM := $(VCPKG_ROOT_NIX)/installed/arm64-osx
VCPKG_INSTALLED_X64 := $(VCPKG_ROOT_NIX)/installed/x64-osx
LIBFIDO2_CFLAGS_ARM64 := -I$(VCPKG_INSTALLED_ARM)/include
LIBFIDO2_LDFLAGS_ARM64 := -L$(VCPKG_INSTALLED_ARM)/lib -lfido2 -lcbor -lssl -lcrypto
LIBFIDO2_CFLAGS_X64 := -I$(VCPKG_INSTALLED_X64)/include
LIBFIDO2_LDFLAGS_X64 := -L$(VCPKG_INSTALLED_X64)/lib -lfido2 -lcbor -lssl -lcrypto
endif
ifeq "${TARGET_OS}" "linux"
LIBFIDO2_LDFLAGS := -lfido2 -lcbor -lssl -lcrypto
endif
ifneq "${DSN_SENTRY}" ""
GO_LDFLAGS+=-X github.com/ProtonMail/proton-bridge/v3/internal/constants.DSNSentry=${DSN_SENTRY}
endif
@ -95,8 +113,14 @@ go-build=go build $(1) -o $(2) $(3)
go-build-finalize=${go-build}
ifeq "${GOOS}-$(shell uname -m)" "darwin-arm64"
go-build-finalize= \
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION_ARM64} CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION_ARM64}" GOARCH=arm64 $(call go-build,$(1),$(2)_arm,$(3)) && \
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION_AMD64} CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION_AMD64}" GOARCH=amd64 $(call go-build,$(1),$(2)_amd,$(3)) && \
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION_ARM64} CGO_ENABLED=1 \
CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION_ARM64} ${LIBFIDO2_CFLAGS_ARM64}" \
CGO_LDFLAGS="${LIBFIDO2_LDFLAGS_ARM64}" \
GOARCH=arm64 $(call go-build,$(1),$(2)_arm,$(3)) && \
MACOSX_DEPLOYMENT_TARGET=${MACOS_MIN_VERSION_AMD64} CGO_ENABLED=1 \
CGO_CFLAGS="-mmacosx-version-min=${MACOS_MIN_VERSION_AMD64} ${LIBFIDO2_CFLAGS_X64}" \
CGO_LDFLAGS="${LIBFIDO2_LDFLAGS_X64}" \
GOARCH=amd64 $(call go-build,$(1),$(2)_amd,$(3)) && \
lipo -create -output $(2) $(2)_arm $(2)_amd && rm -f $(2)_arm $(2)_amd
endif
@ -107,6 +131,14 @@ ifeq "${GOOS}" "windows"
$(if $(4), && rm -f ${4},)
endif
ifneq "${GOOS}" "darwin"
ifneq "${GOOS}" "windows"
go-build-finalize= \
CGO_LDFLAGS="${LIBFIDO2_LDFLAGS}" \
$(call go-build,$(1),$(2),$(3))
endif
endif
${EXE_NAME}: gofiles ${RESOURCE_FILE}
$(call go-build-finalize,${BUILD_FLAGS},"${LAUNCHER_EXE}","./cmd/${TARGET_CMD}/","${ROOT_DIR}/cmd/${TARGET_CMD}/${RESOURCE_FILE}")
mv ${LAUNCHER_EXE} ${BRIDGE_EXE}
@ -137,7 +169,7 @@ ${DEPLOY_DIR}/linux: ${EXE_TARGET} build-launcher
cp -pf ./dist/${EXE_NAME}.desktop ${DEPLOY_DIR}/linux/
mv ${LAUNCHER_EXE} ${DEPLOY_DIR}/linux/
${DEPLOY_DIR}/darwin: ${EXE_TARGET} build-launcher
${DEPLOY_DIR}/darwin: install-libfido2 ${EXE_TARGET} build-launcher
mv ${EXE_GUI_TARGET} ${EXE_TARGET_DARWIN}
mv ${EXE_TARGET} ${DARWINAPP_CONTENTS}/MacOS/${BRIDGE_EXE_NAME}
perl -i -pe"s/>${BRIDGE_GUI_EXE_NAME}/>${LAUNCHER_EXE}/g" ${DARWINAPP_CONTENTS}/Info.plist
@ -411,6 +443,10 @@ clean-vcpkg:
rm -rf ./.git/submodule/vcpkg
rm -rf ./extern/vcpkg
git checkout -- extern/vcpkg
git submodule deinit -f ./extern/vcpkg-windows
rm -rf ./.git/submodule/vcpkg-windows
rm -rf ./extern/vcpkg-windows
git checkout -- extern/vcpkg-windows
clean: clean-vendor clean-gui clean-vcpkg
rm -rf vendor-cache
@ -423,6 +459,15 @@ clean: clean-vendor clean-gui clean-vcpkg
rm -f ${LAUNCHER_EXE} ${BRIDGE_EXE} ${BRIDGE_EXE_NAME}
install-libfido2:
ifeq "${TARGET_OS}" "darwin"
git submodule update --init --recursive ${VCPKG_ROOT_NIX} || \
{ echo "Failed to init vcpkg submodule"; exit 1; }
${VCPKG_ROOT_NIX}/bootstrap-vcpkg.sh -disableMetrics
cd extern/vcpkg && ./vcpkg install libfido2:arm64-osx libfido2:x64-osx
endif
.PHONY: generate
generate:
go generate ./...

View File

@ -1,5 +1,5 @@
# Proton Mail Bridge
Copyright (c) 2025 Proton AG
Copyright (c) 2026 Proton AG
This repository holds the Proton Mail Bridge application.
For a detailed build information see [BUILDS](./BUILDS.md).

View File

@ -1,5 +1,4 @@
---
.script-build:
stage: build
needs: ["lint"]
@ -22,6 +21,7 @@
- bridge_*.tgz
- vault-editor
- bridge-rollout
build-linux:
extends:
- .script-build
@ -33,6 +33,7 @@ build-linux-qa:
- .rules-branch-manual-MR-and-devel-always
variables:
BUILD_TAGS: "build_qa"
VCPKG_MAX_CONCURRENCY: 1
build-darwin:
extends:

View File

@ -1,6 +1,4 @@
---
.env-windows:
extends:
- .image-windows-virt-build
@ -15,10 +13,10 @@
BRIDGE_SYNC_FORCE_MINIMUM_SPEC: 1
VCPKG_DEFAULT_BINARY_CACHE: ${CI_PROJECT_DIR}/.cache
cache:
key: windows-vcpkg-go-0
key: windows-vcpkg-go-1
paths:
- .cache
when: 'always'
when: "always"
.env-darwin:
extends:
@ -36,7 +34,7 @@
key: darwin-go-and-vcpkg
paths:
- .cache
when: 'always'
when: "always"
.env-linux-build:
extends:
@ -47,7 +45,7 @@
key: linux-vcpkg
paths:
- .cache
when: 'always'
when: "always"
before_script:
- export BRIDGE_SYNC_FORCE_MINIMUM_SPEC=1
- !reference [.before-script-git-config, before_script]
@ -56,4 +54,3 @@
- export GOPATH="$CI_PROJECT_DIR/.cache"
tags:
- shared-large

View File

@ -1,7 +1,5 @@
---
include:
- project: 'go/bridge-internal'
ref: 'master'
file: 'ci/runners-setup.yml'
- project: "go/bridge-internal"
ref: "master"
file: "ci/runners-setup.yml"

View File

@ -1,6 +1,4 @@
---
lint:
stage: test
extends:
@ -33,8 +31,6 @@ lint-bug-report-preview:
paths:
- coverage/**
test-linux:
extends:
- .image-linux-test
@ -93,7 +89,6 @@ test-integration-race:
paths:
- integration-race-job.log
test-integration-nightly:
extends:
- test-integration
@ -131,12 +126,43 @@ test-coverage:
paths:
- coverage*
- coverage/**
when: 'always'
when: "always"
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test-e2e-ui:
stage: test
extends:
- .rules-branch-manual-scheduled-and-test-branch-always
tags:
- inbox-hyperv-windows-v1
image: windows-2022-inbox-gui-1.0.0
variables:
REQUIRES_GRAPHICAL_CONSOLE: true
before_script:
- echo "Downloading dotnet dependencies"
- cd ./tests/e2e/ui_tests/windows_os/
- dotnet restore ./ProtonMailBridge.UI.Tests.csproj
- dotnet list package
script:
- |
pwsh "$Env:CI_PROJECT_DIR/tests/e2e/ui_tests/windows_os/InstallerScripts/Get-BridgeInstaller.ps1"
- |
$Env:no_grpc_proxy="127.0.0.1"
$no_grpc_proxy="127.0.0.1"
dotnet test ./ProtonMailBridge.UI.Tests.csproj -- NUnit.Where="cat != TemporarilyExcluded"
after_script:
- |
cp "C:\Users\gitlab-runner\AppData\Roaming\protonmail\bridge-v3\logs\*" "$Env:CI_PROJECT_DIR\tests\e2e\ui_tests\windows_os\Results\artifacts\Logs\"
- |
pwsh "$Env:CI_PROJECT_DIR\tests\e2e\ui_tests\windows_os\InstallerScripts\Remove-Bridge.ps1"
artifacts:
paths:
- tests/e2e/ui_tests/windows_os/Results/artifacts/*
when: always
go-vuln-check:
extends:
- .image-linux-test
@ -150,4 +176,3 @@ go-vuln-check:
when: always
paths:
- vulns*

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -31,6 +31,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/crash"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
@ -164,7 +165,7 @@ func main() { //nolint:funlen
// On windows, if you use Run(), a terminal stays open; we don't want that.
if //goland:noinspection GoBoolExpressions
runtime.GOOS == "windows" {
runtime.GOOS == platform.WINDOWS {
err = cmd.Start()
} else {
err = cmd.Run()

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

2
extern/vcpkg vendored

1
extern/vcpkg-windows vendored Submodule

Submodule extern/vcpkg-windows added at 120deac306

63
go.mod
View File

@ -1,16 +1,16 @@
module github.com/ProtonMail/proton-bridge/v3
go 1.24
go 1.24.4
toolchain go1.24.2
toolchain go1.24.11
require (
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
github.com/Masterminds/semver/v3 v3.2.0
github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1
github.com/ProtonMail/gluon v0.17.1-0.20260112123503-2046c95ca745
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/ProtonMail/go-proton-api v0.4.1-0.20250417134000-e624a080f7ba
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton
github.com/ProtonMail/go-proton-api v0.4.1-0.20260109112619-daf7af47921d
github.com/ProtonMail/gopenpgp/v2 v2.9.0-proton
github.com/PuerkitoBio/goquery v1.8.1
github.com/abiosoft/ishell v2.0.0+incompatible
github.com/allan-simon/go-singleinstance v0.0.0-20210120080615-d0997106ab37
@ -26,41 +26,44 @@ require (
github.com/emersion/go-smtp v0.15.1-0.20221021114529-49b17434419d
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3
github.com/fatih/color v1.13.0
github.com/fxamacker/cbor/v2 v2.9.0
github.com/getsentry/sentry-go v0.15.0
github.com/go-ctap/ctaphid v0.8.1
github.com/go-ctap/winhello v0.1.0
github.com/go-resty/resty/v2 v2.7.0
github.com/godbus/dbus v4.1.0+incompatible
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.3.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/hashicorp/go-multierror v1.1.1
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba
github.com/jeandeaual/go-locale v0.0.0-20220711133428-7de61946b173
github.com/keybase/go-keychain v0.0.0
github.com/keys-pub/go-libfido2 v1.5.4-0.20250104233141-2534349bd685
github.com/miekg/dns v1.1.50
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
github.com/sirupsen/logrus v1.9.2
github.com/stretchr/testify v1.8.4
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.11.1
github.com/urfave/cli/v2 v2.24.4
github.com/vmihailenco/msgpack/v5 v5.3.5
go.uber.org/goleak v1.2.1
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.38.0
golang.org/x/oauth2 v0.7.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
golang.org/x/net v0.42.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sys v0.35.0
golang.org/x/text v0.28.0
google.golang.org/api v0.114.0
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.33.0
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.6
howett.net/plist v1.0.0
)
require (
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf // indirect
github.com/ProtonMail/go-crypto v1.1.4-proton // indirect
github.com/ProtonMail/go-crypto v1.3.0-proton // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/ProtonMail/go-srp v0.0.7 // indirect
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
@ -68,7 +71,7 @@ require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/chzyer/test v1.0.0 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cronokirby/saferith v0.33.0 // indirect
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
@ -87,7 +90,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
@ -98,6 +101,7 @@ require (
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/ldclabs/cose v1.3.2 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
@ -114,27 +118,30 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/tools v0.35.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace (
github.com/ProtonMail/go-autostart => github.com/ElectroNafta/go-autostart v0.0.0-20250402094843-326608c16033
github.com/ProtonMail/go-autostart => github.com/ProtonMail/go-autostart v0.0.0-20250402094843-326608c16033
github.com/emersion/go-message => github.com/ProtonMail/go-message v0.13.1-0.20240919135104-3bc88e6a9423
github.com/emersion/go-smtp => github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865
github.com/go-resty/resty/v2 => github.com/LBeernaertProton/resty/v2 v2.0.0-20231129100320-dddf8030d93a
github.com/keybase/go-keychain => github.com/cuthix/go-keychain v0.0.0-20240103134243-0b6a41580b77
github.com/go-ctap/winhello => github.com/ProtonMail/winhello v0.0.0-20250918145518-a739b7dc2e56
github.com/go-resty/resty/v2 => github.com/ProtonMail/resty/v2 v2.0.0-20250929142426-e3dc6308c80b
github.com/keybase/go-keychain => github.com/ProtonMail/go-keychain v0.0.0-20250929142014-ea8548dff768
github.com/keys-pub/go-libfido2 => github.com/ProtonMail/go-libfido2 v0.0.0-20250916110427-df894d6d07a1
)

150
go.sum
View File

@ -7,10 +7,8 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
@ -23,36 +21,50 @@ github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557 h1:l6surSnJ3RP4qA
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557/go.mod h1:sTrmvD/TxuypdOERsDOS7SndZg0rzzcCi1b6wQMXUYM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ElectroNafta/go-autostart v0.0.0-20250402094843-326608c16033 h1:d2RB9rQmSusb0K+qSgB+DAY+8i+AXZ/o+oDHj2vAUaA=
github.com/ElectroNafta/go-autostart v0.0.0-20250402094843-326608c16033/go.mod h1:o0nKiWcK0e2G/90uL6akWRkzOV4mFcZmvpBPpigJvdw=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/LBeernaertProton/resty/v2 v2.0.0-20231129100320-dddf8030d93a h1:eQO/GF/+H8/9udc9QAgieFr+jr1tjXlJo35RAhsUbWY=
github.com/LBeernaertProton/resty/v2 v2.0.0-20231129100320-dddf8030d93a/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs69zUkSzubzjBbL+cmOXgnmt9Fyd9ug=
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1 h1:FvkPBZF/M5GpZTy+hzhaheyi+Z5XWeZOL5GKVKqj85Y=
github.com/ProtonMail/gluon v0.17.1-0.20250604083016-c6e17f8461b1/go.mod h1:0/c03TzZPNiSgY5UDJK1iRDkjlDPwWugxTT6et2qDu8=
github.com/ProtonMail/gluon v0.17.1-0.20251127091939-17b9426ae8f7 h1:PaqJBeXv30G45LFglNMUxChxzGPg+V870BplSGrt0RM=
github.com/ProtonMail/gluon v0.17.1-0.20251127091939-17b9426ae8f7/go.mod h1:OMwmLjgk6yJHX/P5KPck9WOcBVWIJLvuGZjj/8Ts/cw=
github.com/ProtonMail/gluon v0.17.1-0.20260108112233-b3e52866fa57 h1:aH0EeiBq/5c1rNI/1xzAmJWKgf+nFcqrKCUTUUV4/Sc=
github.com/ProtonMail/gluon v0.17.1-0.20260108112233-b3e52866fa57/go.mod h1:YbW3CyxVxdbXiEGBwOxTW9nczPa8tA58HMkxosSf8bw=
github.com/ProtonMail/gluon v0.17.1-0.20260112123503-2046c95ca745 h1:SHUpYnPoW78xQYAZCCcONiFMJDopk1a7vn7P42kYKMM=
github.com/ProtonMail/gluon v0.17.1-0.20260112123503-2046c95ca745/go.mod h1:YbW3CyxVxdbXiEGBwOxTW9nczPa8tA58HMkxosSf8bw=
github.com/ProtonMail/go-autostart v0.0.0-20250402094843-326608c16033 h1:4r/ALoiixOOyjc1WhpwlkrcSFtRnc1GHWhk7ERELwbs=
github.com/ProtonMail/go-autostart v0.0.0-20250402094843-326608c16033/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/ProtonMail/go-crypto v1.1.4-proton h1:KIo9uNlk3vzlwI7o5VjhiEjI4Ld1TDixOMnoNZyfpFE=
github.com/ProtonMail/go-crypto v1.1.4-proton/go.mod h1:zNoyBJW3p/yVWiHNZgfTF9VsjwqYof5YY0M9kt2QaX0=
github.com/ProtonMail/go-crypto v1.3.0-proton h1:tAQKQRZX/73VmzK6yHSCaRUOvS/3OYSQzhXQsrR7yUM=
github.com/ProtonMail/go-crypto v1.3.0-proton/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-keychain v0.0.0-20250929142014-ea8548dff768 h1:fTHlbASGfwJ4x4EeQ/JX7CX0YBfbFEcYm2nA6puPaWs=
github.com/ProtonMail/go-keychain v0.0.0-20250929142014-ea8548dff768/go.mod h1:ZoZU1fnBy3mOLWr3Pg+Y2+nTKtu6ypDte2kZg9HvSwY=
github.com/ProtonMail/go-libfido2 v0.0.0-20250916110427-df894d6d07a1 h1:MpPKmpti7MswJG5il3A+P24+iGxMj8V7/3JSMSRM1+c=
github.com/ProtonMail/go-libfido2 v0.0.0-20250916110427-df894d6d07a1/go.mod h1:92J9LtSBl0UyUWljElJpTbMMNhC6VeY8dshsu40qjjo=
github.com/ProtonMail/go-message v0.13.1-0.20240919135104-3bc88e6a9423 h1:p8nBDxvRnvDOyrcePKkPpErWGhDoTqpX8a1c54CcSu0=
github.com/ProtonMail/go-message v0.13.1-0.20240919135104-3bc88e6a9423/go.mod h1:NBAn21zgCJ/52WLDyed18YvYFm5tEoeDauubFqLokM4=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250417134000-e624a080f7ba h1:DFBngZ7u/f69flRFzPp6Ipo6PKEyflJlA5OCh52yDB4=
github.com/ProtonMail/go-proton-api v0.4.1-0.20250417134000-e624a080f7ba/go.mod h1:eXIoLyIHxvPo8Kd9e1ygYIrAwbeWJhLi3vgSz2crlK4=
github.com/ProtonMail/go-proton-api v0.4.1-0.20251127095056-9039cd6bf32a h1:g5A/1Jg7JR8MXucKDUJv48LnXq1mOSlI2yXo6/X4R/s=
github.com/ProtonMail/go-proton-api v0.4.1-0.20251127095056-9039cd6bf32a/go.mod h1:Xv7eeoGjaOLMZcjJj++yWNV99q5enByr0WcuF/ltTRA=
github.com/ProtonMail/go-proton-api v0.4.1-0.20260108112223-c9e6b92ad1fc h1:azlBBcGC5Y6FuEFRCY16pXh8vy268C9JBS6oU/AA33k=
github.com/ProtonMail/go-proton-api v0.4.1-0.20260108112223-c9e6b92ad1fc/go.mod h1:aVHyE5kG38rm99RQYuP3wWn8QuJpM5Me6KHaIDD92Qs=
github.com/ProtonMail/go-proton-api v0.4.1-0.20260109112619-daf7af47921d h1:q8y/G0qLRTxZr1xXk/kKFd2xwiyq44Yn2vI0xJJ6bhA=
github.com/ProtonMail/go-proton-api v0.4.1-0.20260109112619-daf7af47921d/go.mod h1:aVHyE5kG38rm99RQYuP3wWn8QuJpM5Me6KHaIDD92Qs=
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865 h1:EP1gnxLL5Z7xBSymE9nSTM27nRYINuvssAtDmG0suD8=
github.com/ProtonMail/go-smtp v0.0.0-20231109081432-2b3d50599865/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/ProtonMail/go-srp v0.0.7 h1:Sos3Qk+th4tQR64vsxGIxYpN3rdnG9Wf9K4ZloC1JrI=
github.com/ProtonMail/go-srp v0.0.7/go.mod h1:giCp+7qRnMIcCvI6V6U3S1lDDXDQYx2ewJ6F/9wdlJk=
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton h1:MMVgE6nk5Ulh9Ud5L1Xc5iaPKE85FbfKQV17ZMucrR0=
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton/go.mod h1:+PjybET6fgcLzldFy1hpy7s8VibZ0T1hLFbxnnMk0lo=
github.com/ProtonMail/gopenpgp/v2 v2.9.0-proton h1:K3YRIBJo3YVObikaV9y1KWYGxFWRML+pFaiyh8ON2xA=
github.com/ProtonMail/gopenpgp/v2 v2.9.0-proton/go.mod h1:NJ4RywdeD2sXCJyRRwb0ZYCx+QwGi14HUmlyNPegiwI=
github.com/ProtonMail/resty/v2 v2.0.0-20250929142426-e3dc6308c80b h1:0GYNP0odNPJFn1fbfwthcYPd3it0AVntvSaCjh2nlaE=
github.com/ProtonMail/resty/v2 v2.0.0-20250929142426-e3dc6308c80b/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/ProtonMail/winhello v0.0.0-20250918145518-a739b7dc2e56 h1:OFDKuwogje2Nor+2X81P0wWGcSZPXu2HKNUE71o3qZI=
github.com/ProtonMail/winhello v0.0.0-20250918145518-a739b7dc2e56/go.mod h1:kJnpbFRhpEatnRc05/CTeq4cWR2LUE7P6+KsPP/zRnE=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
@ -94,8 +106,8 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -114,8 +126,6 @@ github.com/cucumber/godog v0.12.5/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6T
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cuthix/go-keychain v0.0.0-20240103134243-0b6a41580b77 h1:sdB/yJMbubPQothFl6KYCOrMBRgy0pZbBXIWoJqSFLo=
github.com/cuthix/go-keychain v0.0.0-20240103134243-0b6a41580b77/go.mod h1:ZoZU1fnBy3mOLWr3Pg+Y2+nTKtu6ypDte2kZg9HvSwY=
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -161,6 +171,8 @@ github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWT
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/fyne-io/mobile v0.1.2-0.20201127155338-06aeb98410cc/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
@ -172,6 +184,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-ctap/ctaphid v0.8.1 h1:HIDoSfqInkUIRBVPv61fVB2CNZ5nYxoaIgqmt8vzcs4=
github.com/go-ctap/ctaphid v0.8.1/go.mod h1:jRVrVfCs30jdZkSH2PoBopv9ry+tK99mpYumE4GIbb8=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
@ -180,6 +194,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -194,6 +212,8 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goforj/godump v1.6.0 h1:3Dn8gaw5Xxxefr1ezTGTWrTKSr3ihK+eJ2xzRUoFfHQ=
github.com/goforj/godump v1.6.0/go.mod h1:/Vy+p50JtOkwsFN5dA1HQ7LS5gtPk3f61DaP4UR2o4s=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -221,9 +241,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -232,9 +251,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -243,8 +261,8 @@ github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -325,6 +343,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ldclabs/cose v1.3.2 h1:9M5l1zTvOyZONRsNj2PWJjmLdRqkcrsp80tyuNkOHdE=
github.com/ldclabs/cose v1.3.2/go.mod h1:X1srvv76GKudjv85VCUgka049gaK5aozbBhMDaCEbpc=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
@ -418,8 +438,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@ -441,8 +461,9 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02n
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@ -453,8 +474,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@ -467,6 +488,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
@ -480,6 +503,18 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
@ -498,8 +533,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -525,8 +560,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -555,13 +590,13 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -571,8 +606,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -611,8 +646,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -632,8 +667,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -663,12 +698,14 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@ -701,8 +738,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -712,10 +749,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -732,6 +767,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -39,7 +39,10 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/frontend/theme"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
@ -285,56 +288,61 @@ func run(c *cli.Context) error {
logrus.WithError(err).Error("Failed to get settings path")
}
featureFlags := unleash.GetStartupFeatureFlagsAndStore(constants.APIHost, version, locations.ProvideUnleashStartupCachePath)
return withSingleInstance(settings, locations.GetLockFile(), version, func() error {
// Look for available keychains
return WithKeychainList(crashHandler, func(keychains *keychain.List) error {
// Unlock the encrypted vault.
return WithVault(reporter, locations, keychains, crashHandler, func(v *vault.Vault, insecure, corrupt bool) error {
if !v.Migrated() {
// Migrate old settings into the vault.
if err := migrateOldSettings(v); err != nil {
logrus.WithError(err).Error("Failed to migrate old settings")
}
// Migrate old accounts into the vault.
if err := migrateOldAccounts(locations, keychains, v); err != nil {
logrus.WithError(err).Error("Failed to migrate old accounts")
}
// The vault has been migrated.
if err := v.SetMigrated(); err != nil {
logrus.WithError(err).Error("Failed to mark vault as migrated")
}
}
logrus.WithFields(logrus.Fields{
"lastVersion": v.GetLastVersion().String(),
"showAllMail": v.GetShowAllMail(),
"updateCh": v.GetUpdateChannel(),
"autoUpdate": v.GetAutoUpdate(),
"rollout": v.GetUpdateRollout(),
"DoH": v.GetProxyAllowed(),
}).Info("Vault loaded")
// Load the cookies from the vault.
return withCookieJar(v, func(cookieJar http.CookieJar) error {
// Create a new bridge instance.
return withBridge(c, exe, locations, version, identifier, crashHandler, reporter, v, cookieJar, keychains, func(b *bridge.Bridge, eventCh <-chan events.Event) error {
if insecure {
logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted")
b.PushError(bridge.ErrVaultInsecure)
// Pre-init the observability service, load the cached metrics.
return observability.WithObservability(locations, func(obsService *observability.Service) error {
// Unlock the encrypted vault.
return WithVault(reporter, locations, keychains, obsService, featureFlags, crashHandler, func(v *vault.Vault, insecure, corrupt bool) error {
if !v.Migrated() {
// Migrate old settings into the vault.
if err := migrateOldSettings(v); err != nil {
logrus.WithError(err).Error("Failed to migrate old settings")
}
if corrupt {
logrus.Warn("The vault is corrupt and has been wiped")
b.PushError(bridge.ErrVaultCorrupt)
// Migrate old accounts into the vault.
if err := migrateOldAccounts(locations, keychains, v); err != nil {
logrus.WithError(err).Error("Failed to migrate old accounts")
}
// Remove old updates files
b.RemoveOldUpdates()
// The vault has been migrated.
if err := v.SetMigrated(); err != nil {
logrus.WithError(err).Error("Failed to mark vault as migrated")
}
}
// Run the frontend.
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
logrus.WithFields(logrus.Fields{
"lastVersion": v.GetLastVersion().String(),
"showAllMail": v.GetShowAllMail(),
"updateCh": v.GetUpdateChannel(),
"autoUpdate": v.GetAutoUpdate(),
"rollout": v.GetUpdateRollout(),
"DoH": v.GetProxyAllowed(),
}).Info("Vault loaded")
// Load the cookies from the vault.
return withCookieJar(v, func(cookieJar http.CookieJar) error {
// Create a new bridge instance.
return withBridge(c, exe, locations, version, identifier, obsService, crashHandler, reporter, v, cookieJar, keychains, func(b *bridge.Bridge, eventCh <-chan events.Event) error {
if insecure {
logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted")
b.PushError(bridge.ErrVaultInsecure)
}
if corrupt {
logrus.Warn("The vault is corrupt and has been wiped")
b.PushError(bridge.ErrVaultCorrupt)
}
// Remove old updates files
b.RemoveOldUpdates()
// Run the frontend.
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
})
})
})
})
@ -577,5 +585,5 @@ func setDeviceCookies(jar *cookies.Jar) error {
}
func onMacOS() bool {
return runtime.GOOS == "darwin"
return runtime.GOOS == platform.MACOS
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -33,6 +33,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
@ -52,6 +53,7 @@ func withBridge(
locations *locations.Locations,
version *semver.Version,
identifier *useragent.UserAgent,
obsService *observability.Service,
crashHandler *crash.Handler,
reporter *sentry.Reporter,
vault *vault.Vault,
@ -100,6 +102,7 @@ func withBridge(
updater,
version,
keychains,
obsService,
// The API stuff.
constants.APIHost,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -138,7 +138,14 @@ func migrateOldAccounts(locations *locations.Locations, keychains *keychain.List
if err != nil {
return fmt.Errorf("failed to get helper: %w", err)
}
keychain, _, err := keychain.NewKeychain(helper, "bridge", keychains.GetHelpers(), keychains.GetDefaultHelper())
keychain, _, err := keychain.NewKeychain(
helper, "bridge",
keychains.GetHelpers(),
keychains.GetDefaultHelper(),
0,
make(map[string]bool),
)
if err != nil {
return fmt.Errorf("failed to create keychain: %w", err)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -31,6 +31,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/cookies"
"github.com/ProtonMail/proton-bridge/v3/internal/legacy/credentials"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/ProtonMail/proton-bridge/v3/pkg/algo"
@ -85,7 +86,7 @@ func TestMigratePrefsToVaultWithoutKeys(t *testing.T) {
func TestKeychainMigration(t *testing.T) {
// Migration tested only for linux.
if runtime.GOOS != "linux" {
if runtime.GOOS != platform.LINUX {
return
}
@ -134,7 +135,13 @@ func TestKeychainMigration(t *testing.T) {
func TestUserMigration(t *testing.T) {
kcl := keychain.NewTestKeychainsList()
kc, _, err := keychain.NewKeychain("mock", "bridge", kcl.GetHelpers(), kcl.GetDefaultHelper())
kc, _, err := keychain.NewKeychain(
"mock", "bridge",
kcl.GetHelpers(),
kcl.GetDefaultHelper(),
0,
make(map[string]bool),
)
require.NoError(t, err)
require.NoError(t, kc.Put("brokenID", "broken"))

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -20,25 +20,31 @@ package app
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"path"
"runtime"
"github.com/ProtonMail/gluon/async"
"github.com/ProtonMail/proton-bridge/v3/internal/certs"
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/ProtonMail/proton-bridge/v3/internal/vault/observabilitymetrics"
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
"github.com/sirupsen/logrus"
)
func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, panicHandler async.PanicHandler, fn func(*vault.Vault, bool, bool) error) error {
func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, obsSender observability.BasicSender, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler, fn func(*vault.Vault, bool, bool) error) error {
logrus.Debug("Creating vault")
defer logrus.Debug("Vault stopped")
// Create the encVault.
encVault, insecure, corrupt, err := newVault(reporter, locations, keychains, panicHandler)
encVault, insecure, corrupt, err := newVault(reporter, locations, keychains, obsSender, featureFlags, panicHandler)
if err != nil {
return fmt.Errorf("could not create vault: %w", err)
}
@ -60,7 +66,7 @@ func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keycha
return fn(encVault, insecure, corrupt != nil)
}
func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, panicHandler async.PanicHandler) (*vault.Vault, bool, error, error) {
func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, obsSender observability.BasicSender, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler) (*vault.Vault, bool, error, error) {
vaultDir, err := locations.ProvideSettingsPath()
if err != nil {
return nil, false, nil, fmt.Errorf("could not get vault dir: %w", err)
@ -74,7 +80,14 @@ func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychai
lastUsedHelper string
)
if key, helper, err := loadVaultKey(vaultDir, keychains); err != nil {
if key, helper, err := loadVaultKey(vaultDir, keychains, featureFlags); err != nil {
if errors.Is(err, keychain.ErrPreferredKeychainNotAvailable) {
if err := vault.IncrementKeychainFailedAttemptCount(vaultDir); err != nil {
logrus.WithError(err).Error("Failed to increment failed keychain attempt count")
}
return &vault.Vault{}, false, nil, err
}
if reporter != nil {
if rerr := reporter.ReportMessageWithContext("Could not load/create vault key", map[string]any{
"keychainDefaultHelper": keychains.GetDefaultHelper(),
@ -90,6 +103,9 @@ func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychai
// We store the insecure vault in a separate directory
vaultDir = path.Join(vaultDir, "insecure")
// Schedule the relevant observability metric for sending.
obsSender.AddMetrics(observabilitymetrics.GenerateVaultKeyFetchGenericErrorMetric())
} else {
vaultKey = key
lastUsedHelper = helper
@ -103,29 +119,53 @@ func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychai
userVault, corrupt, err := vault.New(vaultDir, gluonCacheDir, vaultKey, panicHandler)
if err != nil {
obsSender.AddMetrics(observabilitymetrics.GenerateVaultCreationGenericErrorMetric())
return nil, false, corrupt, fmt.Errorf("could not create vault: %w", err)
}
// Remember the last successfully used keychain and store that as the user preference.
if err := vault.SetHelper(vaultDir, lastUsedHelper); err != nil {
logrus.WithError(err).Error("Could not store last used keychain helper")
if corrupt != nil {
obsSender.AddMetrics(observabilitymetrics.GenerateVaultCreationCorruptErrorMetric())
}
// Remember the last successfully used keychain on Linux and store that as the user preference.
if runtime.GOOS == platform.LINUX {
if err := vault.SetHelper(vaultDir, lastUsedHelper); err != nil {
logrus.WithError(err).Error("Could not store last used keychain helper")
}
if err := vault.ResetFailedKeychainAttemptCount(vaultDir); err != nil {
logrus.WithError(err).Error("Could not reset and save failed keychain attempt count")
}
}
return userVault, insecure, corrupt, nil
}
// loadVaultKey - loads the key used to encrypt the vault alongside the keychain helper used to access it.
func loadVaultKey(vaultDir string, keychains *keychain.List) (key []byte, keychainHelper string, err error) {
func loadVaultKey(vaultDir string, keychains *keychain.List, featureFlags unleash.FeatureFlagStartupStore) (key []byte, keychainHelper string, err error) {
keychainHelper, err = vault.GetHelper(vaultDir)
if err != nil {
return nil, keychainHelper, fmt.Errorf("could not get keychain helper: %w", err)
}
kc, keychainHelper, err := keychain.NewKeychain(keychainHelper, constants.KeyChainName, keychains.GetHelpers(), keychains.GetDefaultHelper())
keychainFailedAttemptCount, err := vault.GetKeychainFailedAttemptCount(vaultDir)
if err != nil {
return nil, keychainHelper, fmt.Errorf("could not get keychain failed attempt count: %w", err)
}
kc, keychainHelper, err := keychain.NewKeychain(
keychainHelper, constants.KeyChainName,
keychains.GetHelpers(),
keychains.GetDefaultHelper(),
keychainFailedAttemptCount,
featureFlags,
)
if err != nil {
return nil, keychainHelper, fmt.Errorf("could not create keychain: %w", err)
}
logrus.WithField("keychainHelper", keychainHelper).Info("Initialized keychain helper")
key, err = vault.GetVaultKey(kc)
if err != nil {
if keychain.IsErrKeychainNoItem(err) {
@ -134,6 +174,12 @@ func loadVaultKey(vaultDir string, keychains *keychain.List) (key []byte, keycha
return key, keychainHelper, err
}
if keychain.ShouldRetryPreferredKeychain(featureFlags, keychainHelper) {
if keychainFailedAttemptCount < keychain.MaxFailedKeychainAttemptsLinux {
return nil, keychainHelper, keychain.PreferredKeychainRetryError(keychainFailedAttemptCount)
}
}
return nil, keychainHelper, fmt.Errorf("could not check for vault key: %w", err)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -42,6 +42,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
"github.com/ProtonMail/proton-bridge/v3/internal/identifier"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/imapsmtpserver"
@ -57,6 +58,7 @@ import (
"github.com/bradenaw/juniper/xslices"
"github.com/elastic/go-sysinfo/types"
"github.com/go-resty/resty/v2"
uuid "github.com/google/uuid"
"github.com/sirupsen/logrus"
)
@ -166,6 +168,7 @@ func New(
updater Updater, // the updater to fetch and install updates
curVersion *semver.Version, // the current version of the bridge
keychains *keychain.List, // usable keychains
obsService *observability.Service,
apiURL string, // the URL of the API to use
cookieJar http.CookieJar, // the cookie jar to use
@ -204,6 +207,7 @@ func New(
keychains,
panicHandler,
reporter,
obsService,
api,
identifier,
@ -240,6 +244,7 @@ func newBridge(
keychains *keychain.List,
panicHandler async.PanicHandler,
reporter reporter.Reporter,
obsService *observability.Service,
api *proton.Manager,
identifier identifier.Identifier,
@ -271,9 +276,9 @@ func newBridge(
return nil, fmt.Errorf("failed to create focus service: %w", err)
}
unleashService := unleash.NewBridgeService(ctx, api, locator, panicHandler)
unleashService := unleash.NewBridgeService(ctx, api, locator, panicHandler, vault.GetFeatureFlagStickyKey())
observabilityService := observability.NewService(ctx, panicHandler)
obsService.Initialize(ctx, panicHandler)
bridge := &Bridge{
vault: vault,
@ -315,11 +320,11 @@ func newBridge(
lastVersion: lastVersion,
tasks: tasks,
syncService: syncservice.NewService(panicHandler, observabilityService),
syncService: syncservice.NewService(panicHandler, obsService),
unleashService: unleashService,
observabilityService: observabilityService,
observabilityService: obsService,
notificationStore: notifications.NewStore(locator.ProvideNotificationsCachePath),
@ -334,7 +339,7 @@ func newBridge(
reporter,
uidValidityGenerator,
&bridgeIMAPSMTPTelemetry{b: bridge},
observabilityService,
obsService,
unleashService,
)
@ -686,7 +691,7 @@ func (bridge *Bridge) HasAPIConnection() bool {
// then we verify whether the gluon cache exists using the "new" username (provided by the DB path in this case)
// if so we modify the cache directory in the user vault.
func (bridge *Bridge) verifyUsernameChange() {
if runtime.GOOS != "darwin" {
if runtime.GOOS != platform.MACOS {
return
}
@ -785,3 +790,7 @@ func (bridge *Bridge) SetHostVersionGetterTest(fn func(host types.Host) string)
func (bridge *Bridge) SetRolloutPercentageTest(rollout float64) error {
return bridge.vault.SetUpdateRollout(rollout)
}
func (bridge *Bridge) GetFeatureFlagStickyKey() uuid.UUID {
return bridge.vault.GetFeatureFlagStickyKey()
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -45,6 +45,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/focus"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/services/imapsmtpserver"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/user"
@ -56,6 +57,7 @@ import (
imapid "github.com/emersion/go-imap-id"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
)
@ -778,6 +780,30 @@ func TestBridge_ChangeCacheDirectory(t *testing.T) {
})
}
func TestBridge_FeatureFlagStickyKey_Persistence(t *testing.T) {
var uuidOne uuid.UUID
var uuidTwo uuid.UUID
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, _ *bridge.Mocks) {
uuidOne = b.GetFeatureFlagStickyKey()
})
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, _ *bridge.Mocks) {
require.Equal(t, uuidOne, b.GetFeatureFlagStickyKey())
})
})
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, _ *bridge.Mocks) {
uuidTwo = b.GetFeatureFlagStickyKey()
require.NotEqual(t, uuidOne, uuidTwo)
})
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, vaultKey, func(b *bridge.Bridge, _ *bridge.Mocks) {
require.Equal(t, uuidTwo, b.GetFeatureFlagStickyKey())
})
})
}
func TestBridge_ChangeAddressOrder(t *testing.T) {
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, vaultKey []byte) {
// Create a user.
@ -919,6 +945,7 @@ func withBridgeNoMocks(
mocks.Updater,
v2_3_0,
keychain.NewTestKeychainsList(),
observability.NewTestService(),
// The API stuff.
apiURL,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -88,3 +88,18 @@ func (mr *MockReporterMockRecorder) ReportMessageWithContext(arg0, arg1 interfac
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportMessageWithContext", reflect.TypeOf((*MockReporter)(nil).ReportMessageWithContext), arg0, arg1)
}
// ReportWarningWithContext mocks base method.
func (m *MockReporter) ReportWarningWithContext(arg0 string, arg1 map[string]interface{}) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ReportWarningWithContext", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// ReportWarningWithContext indicates an expected call of ReportWarningWithContext.
func (mr *MockReporterMockRecorder) ReportWarningWithContext(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportWarningWithContext", reflect.TypeOf((*MockReporter)(nil).ReportMessageWithContext), arg0, arg1)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -344,7 +344,12 @@ func (bridge *Bridge) installUpdate(ctx context.Context, job installJob) {
case err != nil:
log.WithError(err).Error("The update could not be installed")
if reporterErr := bridge.reporter.ReportMessageWithContext(
"Cannot install update",
reporter.Context{"error": err},
); reporterErr != nil {
log.WithError(reporterErr).Error("Failed to report update error")
}
bridge.publish(events.UpdateFailed{
Release: job.Release,
Silent: job.Silent,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -28,6 +28,7 @@ import (
"github.com/ProtonMail/go-proton-api/server"
bridgePkg "github.com/ProtonMail/proton-bridge/v3/internal/bridge"
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/updater/versioncompare"
"github.com/elastic/go-sysinfo/types"
@ -331,7 +332,7 @@ func Test_Update_CheckOSVersion_NoUpdate(t *testing.T) {
bridge.CheckForUpdates()
if runtime.GOOS == "darwin" {
if runtime.GOOS == platform.MACOS {
require.Equal(t, events.UpdateNotAvailable{}, <-updateNotAvailableCh)
} else {
require.Equal(t, events.UpdateInstalled{
@ -442,7 +443,7 @@ func Test_Update_CheckOSVersion_HasUpdate(t *testing.T) {
bridge.CheckForUpdates()
if runtime.GOOS == "darwin" {
if runtime.GOOS == platform.MACOS {
require.Equal(t, events.UpdateInstalled{
Release: expectedUpdateRelease,
Silent: true,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -37,6 +37,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/bridge"
"github.com/ProtonMail/proton-bridge/v3/internal/constants"
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/user"
"github.com/bradenaw/juniper/stream"
"github.com/bradenaw/juniper/xslices"
@ -77,7 +78,7 @@ func TestBridge_User_RefreshEvent(t *testing.T) {
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) {
syncCh, closeCh := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{}))
if runtime.GOOS != "windows" {
if runtime.GOOS != platform.WINDOWS {
require.Equal(t, userID, (<-syncCh).UserID)
}
require.Equal(t, userID, (<-syncCh).UserID)

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//
@ -21,6 +21,8 @@ package constants
import (
"fmt"
"runtime"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
)
const VendorName = "protonmail"
@ -72,13 +74,13 @@ const (
// nolint:goconst
func getAPIOS() string {
switch runtime.GOOS {
case "darwin":
case platform.MACOS:
return "macos"
case "linux":
case platform.LINUX:
return "linux"
case "windows":
case platform.WINDOWS:
return "windows"
default:

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//
@ -22,6 +22,8 @@ import (
"crypto/tls"
"net"
"net/http"
"net/url"
"strings"
"time"
)
@ -29,6 +31,11 @@ type TLSDialer interface {
DialTLSContext(ctx context.Context, network, address string) (conn net.Conn, err error)
}
type SecureTLSDialer interface {
DialTLSContext(ctx context.Context, network, address string) (conn net.Conn, err error)
ShouldSkipCertificateChainVerification(address string) bool
}
func SetBasicTransportTimeouts(t *http.Transport) {
t.MaxIdleConns = 100
t.MaxIdleConnsPerHost = 100
@ -71,6 +78,35 @@ func NewBasicTLSDialer(hostURL string) *BasicTLSDialer {
}
}
func extractDomain(hostname string) string {
parts := strings.Split(hostname, ".")
if len(parts) >= 2 {
return strings.Join(parts[len(parts)-2:], ".")
}
return hostname
}
// ShouldSkipCertificateChainVerification determines whether certificate chain validation should be skipped.
// It compares the domain of the requested address with the configured host URL domain.
// Returns true if the domains don't match (skip verification), false if they do (perform verification).
//
// NOTE: This assumes single-part TLDs (.com, .me) and won't handle multi-part TLDs correctly.
func (d *BasicTLSDialer) ShouldSkipCertificateChainVerification(address string) bool {
parsedURL, err := url.Parse(d.hostURL)
if err != nil {
return true
}
addressHost, _, err := net.SplitHostPort(address)
if err != nil {
addressHost = address
}
hostDomain := extractDomain(parsedURL.Host)
addressDomain := extractDomain(addressHost)
return addressDomain != hostDomain
}
// DialTLSContext returns a connection to the given address using the given network.
func (d *BasicTLSDialer) DialTLSContext(ctx context.Context, network, address string) (conn net.Conn, err error) {
return (&tls.Dialer{
@ -78,7 +114,7 @@ func (d *BasicTLSDialer) DialTLSContext(ctx context.Context, network, address st
Timeout: 30 * time.Second,
},
Config: &tls.Config{
InsecureSkipVerify: address != d.hostURL, //nolint:gosec
InsecureSkipVerify: d.ShouldSkipCertificateChainVerification(address), //nolint:gosec
},
}).DialContext(ctx, network, address)
}

View File

@ -0,0 +1,134 @@
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package dialer
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestBasicTLSDialer_ShouldSkipCertificateChainVerification(t *testing.T) {
tests := []struct {
hostURL string
address string
expected bool
}{
{
hostURL: "https://mail-api.proton.me",
address: "mail-api.proton.me:443",
expected: false,
},
{
hostURL: "https://proton.me",
address: "proton.me",
expected: false,
},
{
hostURL: "https://api.proton.me",
address: "mail.proton.me:443",
expected: false,
},
{
hostURL: "https://proton.me",
address: "mail-api.proton.me:443",
expected: false,
},
{
hostURL: "https://mail-api.proton.me",
address: "proton.me:443",
expected: false,
},
{
hostURL: "https://mail.google.com",
address: "mail-api.proton.me:443",
expected: true,
},
{
hostURL: "https://mail-api.protonmail.com",
address: "mail-api.proton.me:443",
expected: true,
},
{
hostURL: "https://proton.me",
address: "google.com:443",
expected: true,
},
{
hostURL: "https://proton.me",
address: "proton.com:443",
expected: true,
},
{
hostURL: "https://proton.me",
address: "example.me:443",
expected: true,
},
{
hostURL: "https://proton.me",
address: "mail.example.com:443",
expected: true,
},
{
hostURL: "https://proton.me",
address: "proton.me",
expected: false,
},
{
hostURL: "https://proton.me:8080",
address: "proton.me:443",
expected: true,
},
{
hostURL: "https://proton.me/api/v1",
address: "proton.me:443",
expected: false,
},
{
hostURL: "https://proton.black",
address: "mail-api.pascal.proton.black",
expected: false,
},
{
hostURL: "https://mail-api.pascal.proton.black",
address: "mail-api.pascal.proton.black",
expected: false,
},
{
hostURL: "https://mail-api.pascal.proton.black",
address: "proton.black:332",
expected: false,
},
{
hostURL: "https://mail-api.pascal.proton.black",
address: "proton.me",
expected: true,
},
{
hostURL: "https://mail-api.pascal.proton.black",
address: "proton.me:332",
expected: true,
},
}
for _, tt := range tests {
dialer := NewBasicTLSDialer(tt.hostURL)
result := dialer.ShouldSkipCertificateChainVerification(tt.address)
require.Equal(t, tt.expected, result)
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -50,12 +50,12 @@ var TrustedAPIPins = []string{ //nolint:gochecknoglobals
}
// TLSReportURI is the address where TLS reports should be sent.
const TLSReportURI = "https://reports.protonmail.ch/reports/tls"
const TLSReportURI = "https://reports.proton.me/reports/tls"
// PinningTLSDialer wraps a TLSDialer to check fingerprints after connecting and
// to report errors if the fingerprint check fails.
type PinningTLSDialer struct {
dialer TLSDialer
dialer SecureTLSDialer
pinChecker PinChecker
reporter Reporter
tlsIssueCh chan struct{}
@ -68,13 +68,13 @@ type Reporter interface {
// PinChecker is used to check TLS keys of connections.
type PinChecker interface {
CheckCertificate(conn net.Conn) error
CheckCertificate(conn net.Conn, certificateChainVerificationSkipped bool) error
}
// NewPinningTLSDialer constructs a new dialer which only returns TCP connections to servers
// which present known certificates.
// It checks pins using the given pinChecker and reports issues using the given reporter.
func NewPinningTLSDialer(dialer TLSDialer, reporter Reporter, pinChecker PinChecker) *PinningTLSDialer {
func NewPinningTLSDialer(dialer SecureTLSDialer, reporter Reporter, pinChecker PinChecker) *PinningTLSDialer {
return &PinningTLSDialer{
dialer: dialer,
pinChecker: pinChecker,
@ -85,6 +85,7 @@ func NewPinningTLSDialer(dialer TLSDialer, reporter Reporter, pinChecker PinChec
// DialTLSContext dials the given network/address, returning an error if the certificates don't match the trusted pins.
func (p *PinningTLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
shouldSkipCertificateChainVerification := p.dialer.ShouldSkipCertificateChainVerification(address)
conn, err := p.dialer.DialTLSContext(ctx, network, address)
if err != nil {
return nil, err
@ -95,7 +96,7 @@ func (p *PinningTLSDialer) DialTLSContext(ctx context.Context, network, address
return nil, err
}
if err := p.pinChecker.CheckCertificate(conn); err != nil {
if err := p.pinChecker.CheckCertificate(conn, shouldSkipCertificateChainVerification); err != nil {
if tlsConn, ok := conn.(*tls.Conn); ok && p.reporter != nil {
p.reporter.ReportCertIssue(TLSReportURI, host, port, tlsConn.ConnectionState())
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//
@ -41,3 +41,15 @@ func NewTLSPinChecker(trustedPins []string) *TLSPinChecker {
func certFingerprint(cert *x509.Certificate) string {
return fmt.Sprintf(`pin-sha256=%q`, algo.HashBase64SHA256(string(cert.RawSubjectPublicKeyInfo)))
}
func (p *TLSPinChecker) isCertFoundInKnownPins(cert *x509.Certificate) bool {
fingerprint := certFingerprint(cert)
for _, pin := range p.trustedPins {
if pin == fingerprint {
return true
}
}
return false
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//
@ -25,8 +25,8 @@ import (
"net"
)
// CheckCertificate returns whether the connection presents a known TLS certificate.
func (p *TLSPinChecker) CheckCertificate(conn net.Conn) error {
// CheckCertificate verifies that the connection presents a known pinned leaf TLS certificate.
func (p *TLSPinChecker) CheckCertificate(conn net.Conn, certificateChainVerificationSkipped bool) error {
tlsConn, ok := conn.(*tls.Conn)
if !ok {
return errors.New("connection is not a TLS connection")
@ -34,14 +34,31 @@ func (p *TLSPinChecker) CheckCertificate(conn net.Conn) error {
connState := tlsConn.ConnectionState()
for _, peerCert := range connState.PeerCertificates {
fingerprint := certFingerprint(peerCert)
// When certificate chain verification is enabled (e.g., for known API hosts), we expect the TLS handshake to produce verified chains.
// We then validate that the leaf certificate of at least one verified chain matches a known pinned public key.
if !certificateChainVerificationSkipped {
if len(connState.VerifiedChains) == 0 {
return errors.New("no verified certificate chains")
}
for _, pin := range p.trustedPins {
if pin == fingerprint {
for _, chain := range connState.VerifiedChains {
// Check if the leaf certificate is one of the trusted pins.
if p.isCertFoundInKnownPins(chain[0]) {
return nil
}
}
return ErrTLSMismatch
}
// When certificate chain verification is skipped (e.g., for DoH proxies using self-signed certs),
// we only validate the leaf certificate against known pinned public keys.
if len(connState.PeerCertificates) == 0 {
return errors.New("no peer certificates available")
}
if p.isCertFoundInKnownPins(connState.PeerCertificates[0]) {
return nil
}
return ErrTLSMismatch

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//
@ -23,6 +23,6 @@ import "net"
// CheckCertificate returns whether the connection presents a known TLS certificate.
// The QA implementation always returns nil.
func (p *TLSPinChecker) CheckCertificate(conn net.Conn) error {
func (p *TLSPinChecker) CheckCertificate(conn net.Conn, _ bool) error {
return nil
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//
@ -64,8 +64,7 @@ func TestTLSPinInvalid(t *testing.T) {
checkTLSIssueHandler(t, 1, called)
}
// Disabled for now we'll need to patch this up.
func _TestTLSPinNoMatch(t *testing.T) { //nolint:unused
func TestTLSPinNoMatch(t *testing.T) {
skipIfProxyIsSet(t)
called, _, reporter, checker, cm := createClientWithPinningDialer(getRootURL())
@ -91,12 +90,22 @@ func TestTLSSignedCertWrongPublicKey(t *testing.T) {
r.Error(t, err, "expected dial to fail because of wrong public key")
}
/*
For the following test the SSL pin rotates from time to time. Thus, the pin needs to be updated accordingly.
A new pin can be extracted by running the following command:
echo | openssl s_client -connect rsa4096.badssl.com:443 2>/dev/null | \
openssl x509 -pubkey -noout | \
openssl pkey -pubin -outform DER | \
openssl dgst -sha256 -binary | \
base64
*/
func TestTLSSignedCertTrustedPublicKey(t *testing.T) {
skipIfProxyIsSet(t)
_, dialer, _, checker, _ := createClientWithPinningDialer("")
copyTrustedPins(checker)
checker.trustedPins = append(checker.trustedPins, `pin-sha256="FlvTPG/nIMKtOj9nelnEjujwSZ5EDyfiKYxZgbXREls="`)
checker.trustedPins = append(checker.trustedPins, `pin-sha256="GGNnH/+pMnSFwy6vEDvfi5TRVWMpC5IINu3BHRM1c4E="`)
_, err := dialer.DialTLSContext(context.Background(), "tcp", "rsa4096.badssl.com:443")
r.NoError(t, err, "expected dial to succeed because public key is known and cert is signed by CA")
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

View File

@ -1,4 +1,4 @@
// Copyright (c) 2025 Proton AG
// Copyright (c) 2026 Proton AG
//
// This file is part of Proton Mail Bridge.
//

Some files were not shown because too many files have changed in this diff Show More