View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0011957 | Taler | wallet (iOS App) | public | 2026-11-06 10:11 | 2026-11-06 10:11 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Product Version | 1.6 | ||||
| Summary | 0011957: The authentication lock does not hide what it locks: the screen underneath stays in the accessibility tree, fully labelled and marked enabled | ||||
| Description | With "Use FaceID" on and the wallet locked, `MainView` draws the lock state as an overlay over the live content: }.overlay { if locked { Color.gray.opacity(0.75) ... if let errorMessage = biometricService.authenticationError { Text(errorMessage) ... } else { Image(TALER_LOGO) ... } Two consequences, both measured rather than inferred. 1. The scrim is 75% opaque, so the locked screen stays partly legible. 2. More seriously, nothing is hidden from the accessibility tree. There is no `.accessibilityHidden(true)` on the content being covered, so every element of the screen underneath remains present, labelled, and reported **enabled** while the wallet is locked. Evidence -------- Episode `camp-e00008`, screens 40 to 42, all three carrying the lock overlay's own text "You canceled the authentication.". The recorded accessibility tree of screen 40 has **62 elements with text**, which is the whole of the Settings screen underneath. An extract, verbatim from the recording, with frames: navigationBar Settings [0, 62, 402, 106] button Balances [16, 62, 44, 44] button About GNU Taler [16, 168, 370, 74] button Payment Services, Manage payment services [16, 242, 370, 84] button Bank Accounts, Your accounts for deposit [16, 326, 370, 84] switch Use FaceID, Protect your money [16, 410, 370, 84] button Taler Directory, Your payment contacts [16, 494, 370, 84] and on each of screens 40, 41 and 42 the controls `Balances`, `About GNU Taler`, `Payment Services`, `Bank Accounts` and `Use FaceID` are all `enabled: true`. So while the wallet is locked: * VoiceOver reads the locked screen aloud; * any accessibility client can enumerate it -- which is how this was found: an automated UI walk recorded it; * the controls report themselves as enabled. What I have **not** established is whether a control under the overlay can actually be activated through it. SwiftUI's `.overlay` places the scrim in front for hit-testing, so it probably cannot; but "reports enabled" and "cannot be hit" is a distinction the accessibility tree does not make, and an assistive technology that activates by element rather than by coordinate is the case to check. I am reporting what the tree says and flagging that question rather than claiming a bypass. A second, smaller defect in the same block ------------------------------------------ On a device that cannot authenticate -- which includes any simulator with no enrolled biometrics -- turning the toggle on puts the red error text up and then, three seconds later, silently switches the setting back off: .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { biometricService.authenticationError = nil if !biometricService.canAuthenticate { print("authentication not available") useAuthentication = false // switch off } } } The user sees a red message disappear and a switch they just turned on turn itself off, with no explanation; the explanation goes to `print`. Better would be to refuse the toggle at the point of use with a reason, or to leave it on and say why it cannot take effect. How to reproduce ---------------- By hand: 1. Settings -> turn "Use FaceID" on, on a device with no enrolled biometrics (any simulator) 2. the gray lock overlay appears over Settings with a red message 3. with VoiceOver on, swipe through: the Settings rows underneath are still read out 4. after ~3 seconds the overlay goes and the switch is off again With the exploration harness, from `harness/` on a Mac with Xcode: python3 -m taler_fuzz.cli build python3 -m taler_fuzz.cli --prefer "settings,faceid,protect,use" \ --max-steps 40 replay --label lock --shots The condition is reached whenever a walk flips that switch; it was recorded 128 times across the campaign as an overlap finding before being understood. Suggested fix ------------- Apply `.accessibilityHidden(locked)` (or `.accessibilityElement(children: .ignore)`) to the content being covered, and make the scrim opaque -- or use `.privacySensitive()` / `.redacted(reason: .privacy)` -- so that "locked" means the content is neither readable nor enumerable. No patch attached: which of those the maintainer wants is a design decision, and the accessibility change has to be made where the overlay is applied rather than inside it. Estimated severity ------------------ High for the accessibility exposure -- an authentication gate that leaves the protected content enumerable does not do the one thing it exists for. Low for the silent self-disabling toggle. Estimated effort to fix ----------------------- Small -- one modifier for the accessibility half, plus a decision about the scrim. The toggle behaviour is a separate, slightly larger UX change. Filed by an agent (bot-cg) on behalf of Christian Grothoff, from an automated UI exploration campaign. Left unassigned deliberately. | ||||
| Tags | No tags attached. | ||||
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-06 10:11 | bot-cg | New Issue |