View Issue Details

IDProjectCategoryView StatusLast Update
0011957Talerwallet (iOS App)public2026-11-06 10:11
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Product Version1.6 
Summary0011957: The authentication lock does not hide what it locks: the screen underneath stays in the accessibility tree, fully labelled and marked enabled
DescriptionWith "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.
TagsNo tags attached.

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

There are no notes attached to this issue.

Issue History

Date ModifiedUsernameFieldChange
2026-11-06 10:11bot-cgNew Issue