Because:
* GET /account is gated only by a bare sessionToken, with no tokenVerified
or assurance-level check. A caller holding nothing but the account
password therefore received the unmasked E.164 number in
recoveryPhone.phoneNumber, which is the credential needed to mount a SIM
swap and defeat TOTP.
* GET /recovery_phone decided whether to mask from emailVerified,
mustVerify and tokenVerified, and never consulted the session's
assurance level, so a session that cleared those checks without reaching
AAL2 also received the full number.
This commit:
* Adds RecoveryPhoneService.hasConfirmedMasked(), returning only the last
four digits, and points both read endpoints at it. Masking is
unconditional rather than derived from the caller's assurance level, so
there is no gate left to get wrong.
* Omits nationalFormat from both read responses. Twilio's grouping would
reveal the number's length and region, and clients render the last four
through the localized recovery-phone-number-ending-digits string rather
than formatting them, so the formatted value has no consumer.
* Drops the phoneNumberStrip parameter from hasConfirmed(). Defaulting it
to "do not mask" is what made the leak possible. The raw accessor is now
documented server-side only and is reached by the email, setup and
change paths, which need a dialable number.
* Constrains phoneNumber in both response schemas through a new
maskedPhoneNumber validator, and drops nationalFormat from them
entirely, so a future regression fails response validation instead of
shipping.
* Routes PageRecoveryPhoneRemove through formatPhoneNumber, which
otherwise rendered a bare "1234" now that no national format arrives.
Settings previously showed a client-masked national format and now shows
"Number ending in 1234", matching what the signin and reset-password
recovery screens already displayed. The display is consistent, localized
and safe to render in RTL locales.
The confirm and change endpoints still return a dialable number. Both are
gated on a verified session or mfa:2fa and echo a number the caller just
supplied and proved control of, so they are not an egress path.
Closes FXA-14028