feat: backup brute-force detection via key-server attempts - #2560
feat: backup brute-force detection via key-server attempts#2560ethicnology wants to merge 2 commits into
Conversation
|
Claude finished @ethicnology's task in 7m 51s —— View job Code review — backup brute-force detection via key-server telemetry
Overall this is a carefully built feature: the trust-model discipline in the copy, the targeted-vs-global 429 separation, the wipe detection, and the migration idempotency guards are all well done. The findings below are ordered by severity; the top three are correctness/security issues worth resolving before merge. 🔴 1. Local
|
c7cd2be to
a92534e
Compare
Adds the core of the key-server brute-force telemetry integration (recoverbull-client-dart telemetry PR), keeping telemetry strictly advisory: the server cannot distinguish an attacker from the user or another of the user's devices, and a compromised server can fabricate or suppress counters. Everything warns, never acts automatically. Domain and data: - Domain mirrors of the SDK telemetry types (KeyServerAttemptStatus, VaultKeyFetchResult, TelemetrySnapshotResult, KeyServerInfo) and a sealed RecoverbullTelemetryAlert entity (SuspiciousActivity, TargetedLockout, ServicePressure, TelemetryUnavailable, CountersWiped). The core layer stays free of SDK types. - New failures for the client's dedicated 429/503 subtypes, mapped in the repository: targeted lockout (alarm) vs global overload and capacity vs busy 503 (service pressure, never an attack). - Repository: status-aware fetch/trash, conditional /attempts, server info, and the telemetry baseline persistence. Persistence (single drift migration, 14 -> 15): - recoverbull_telemetry_server (per-URL polling state: ETag, last check, collection_started_at, consecutive failures) and recoverbull_telemetry_backup (per-backup own-operation counters with window tracking, warning dedup and acknowledgement). Only sha256(raw backup id) is stored, never the raw identifier. - is_recoverbull_telemetry_enabled settings flag (default false), accessed through targeted datasource accessors — deliberately not part of SettingsModel/SettingsEntity to avoid rippling into every settings constructor call site. Orchestration: - CheckBackupTelemetryUsecase: cold-launch conditional poll with staleness skip, per-backup reconciliation (server total vs this device's own count, window-rollover aware), wipe detection via collection_started_at (resets the baseline, never an attack alarm), prolonged-unavailability soft warning, and service-pressure mapping. - RecordLocalAttemptUsecase: counts this device's own operations and surfaces immediate suspicion from attempt_status. - AcknowledgeTelemetryAlertUsecase and the app-scoped RecoverbullTelemetryCubit holding the (ephemeral) alerts with persisted dedup. - Changing the key-server URL invalidates the old server's baseline; ResetAppDataUsecase wipes it. Usecase, repository and migration tests included.
Wires the telemetry orchestration into the app and surfaces the alerts. Checks: - Cold launch: AppStartupBloc fires RecoverbullTelemetryCubit .checkOnColdLaunch() unawaited after Tor init — never blocking startup, a no-op when the flag is off, Tor is not ready, or the last check is still fresh. No background polling. - Backup flows: the recoverbull feature records this device's own operations (store, and fetch via the new status-aware usecase) and reports the targeted per-identifier lockout as an alarm signal. Global 429 / capacity 503 map to unavailability, never to a lockout alarm. Surfacing (all advisory, all dismissible): - RecoverbullTelemetryWarnings banner on the wallet home and the backup settings screen; strong warnings (suspicious activity, targeted lockout) also open a bottom sheet once per session. Acknowledgement is remembered so multi-device false positives do not train the user to ignore alerts. - Copy follows the trust model: 'unknown/suspicious activity', never 'confirmed attack', always paired with 'check whether it was you or another of your devices' and 'never enter your seed words or password because of this alert'. English and French localization. The feature stays behind is_recoverbull_telemetry_enabled (default false) until the server is deployed and the client pinned by SHA. Bloc test updated for the new dependencies.
a92534e to
290d331
Compare
A Bull user whose Backup File leaks has no idea their backup is being probed:
an attacker gets 3 password guesses per cooldown window, or can grief the
victim by keeping their identifier permanently rate-limited — and the user
finds out only when recovery fails. The key server now publishes advisory
telemetry (
/attemptssnapshot,attempt_statuson successful fetch/trash,wipe metadata on
/info). This PR integrates it into Bull as warningsonly, per the protocol's trust model: the server cannot distinguish an
attacker from the user or another of the user's devices, and a compromised
server can fabricate or suppress counters.
Depends on: recoverbull-client-dart telemetry PR and the deployed
recoverbull-server. The
recoverbulldependency currently uses a localpath:(dev convenience); it is pinned to the reviewed merge SHA in afollow-up
chorecommit once the client PR merges.Commits
Commit 1 —
feat: brute-force telemetry core and check orchestrationThe whole core layer, self-contained (compiles and tests pass standalone).
KeyServerAttemptStatus,VaultKeyFetchResult,TelemetrySnapshotResult,KeyServerInfo) and asealed
RecoverbullTelemetryAlertentity — the core layer stays free ofSDK types. New failures for the client's dedicated 429/503 subtypes, mapped
in the repository: targeted lockout (alarm) vs global overload and capacity
vs busy 503 (service pressure, never an attack).
invalidated when the URL changes:
recoverbull_telemetry_server(ETag,last check,
collection_started_at, consecutive failures) andrecoverbull_telemetry_backup(this device's own operation counters withwindow tracking, warning dedup, acknowledgement). Only
sha256(raw backup id)is stored, never the raw identifier — the baselinestill reveals which backups are monitored, so it lives in the same
protected store as other sensitive app state.
is_recoverbull_telemetry_enabledsettings flag (default false),accessed through targeted datasource accessors — deliberately not part of
SettingsModel/SettingsEntityto avoid rippling into every settingsconstructor call site.
CheckBackupTelemetryUsecase(cold-launch conditionalpoll with staleness skip, per-backup window-rollover-aware reconciliation,
wipe detection that resets the baseline without an attack alarm,
prolonged-unavailability soft warning, service-pressure mapping),
RecordLocalAttemptUsecase(counts this device's operations, surfacesimmediate suspicion from
attempt_status),AcknowledgeTelemetryAlertUsecase,and the app-scoped
RecoverbullTelemetryCubitholding the ephemeral alertswith persisted dedup. Changing the key-server URL invalidates the old
server's baseline;
ResetAppDataUsecasewipes it.Commit 2 —
feat: wire telemetry checks into startup and backup flows, warnings UIAppStartupBlocfirescheckOnColdLaunch()unawaitedafter Tor init — never blocking startup, a no-op when the flag is off, Tor
is not ready, or the last check is still fresh. No background polling.
operations (store, and fetch via the new status-aware usecase — the
freshest signal, available even when
/attemptsis overloaded) and reportsthe targeted per-identifier lockout as an alarm. Global 429 / capacity 503
map to unavailability, never to a lockout alarm.
and the backup settings screen; strong warnings also open a bottom sheet
once per session. Acknowledgement is remembered so multi-device false
positives do not train the user to ignore alerts.
Warning rules:
attempt_statusshows attempts the user did not make429on the user's own fetch429, capacity503, nearly-full snapshot (vsmax_attempt_identifiersfrom/info)/attemptsunreachable for several dayscollection_started_atMessaging rules (from the protocol threat model — the alarm itself is an
attack vector for phishing and panic):
devices first" — another device is the expected false positive.
in the copy.
wallet, recommend rotating keys / moving funds — as a user-initiated,
guided action, never automatic.
Test plan
causes no snapshot processing.
planted-row attempts are caught through the total counter.
429and global429produce different surfaces; capacity503maps to service pressure.
collection_started_atresets the baseline without an attackalarm; regressed counters and disappeared entries stay silent.
surfaces service pressure.
collection_started_at;ResetAppDataUsecasewipes both tables.flutter analyzeclean.Deployment note
Server deployment and the client release must precede enabling this feature.
The feature flag (
is_recoverbull_telemetry_enabled, default false) staysoff until the pinned client commit and the deployed server contract are
confirmed in production.