plugin: trezor: handle empty passphrase feature in settings dialog
What changed, and why it matters
This commit fixes a crash in Electrum's settings window for Trezor hardware wallets. After a user disabled the passphrase feature and disconnected the device, the settings dialog tried to read a value that had become 'None', causing a TypeError and crashing the dialog. The patch stores the device features before disconnecting and gracefully handles the 'None' case by showing 'Unknown' instead of crashing.
No security action required; treat as a normal bugfix. Users may update to a version containing this commit if they experienced the settings dialog crash.
Security signals we found
UI crash / unhandled exception in hardware wallet plugin settings dialog
None-value dereference (TypeError) in list indexing
No evidence of malicious control flow, privilege escalation, or data exposure
Evidence from the diff
In electrum/plugins/trezor/qt.py, the Trezor settings dialog’s update(features) callback indexed into the disen/endis arrays using features.passphrase_protection. After unpairing/disconnecting, this attribute can be None, causing a TypeError. The patch (1) captures client.features before unpairing inside a try/finally so cleanup still occurs, (2) checks for None and displays ‘Unknown’ for the passphrase protection label, and (3) coerces the value to bool for button text and disables the button when the value is None. This is a UI robustness fix, not a security vulnerability.
Changed components
electrum/plugins/trezor/qt.pyTrezor hardware wallet plugin Qt settings dialogInspect captured patch +10 / −5
### electrum/plugins/trezor/qt.py
@@ -506,9 +506,12 @@ def task():
raise RuntimeError("Device not connected")
if method:
getattr(client, method)(*args, **kw_args)
- if unpair_after:
- devmgr.unpair_id(device_id)
- return client.features
+ try:
+ features = client.features # some features are set None after unpairing, so store them first
+ finally: # always clean up
+ if unpair_after:
+ devmgr.unpair_id(device_id)
+ return features
thread.add(task, on_success=update)
@@ -531,7 +534,8 @@ def update(features):
device_label.setText(features.label)
pin_set_label.setText(noyes[features.pin_protection])
- passphrases_label.setText(disen[features.passphrase_protection])
+ passphrase_protection = features.passphrase_protection # might be None if device is locked
+ passphrases_label.setText(disen[passphrase_protection] if passphrase_protection is not None else _("Unknown"))
bl_hash_label.setText(bl_hash)
label_edit.setText(features.label)
device_id_label.setText(features.device_id)
@@ -541,7 +545,8 @@ def update(features):
clear_pin_warning.setVisible(features.pin_protection)
pin_button.setText(setchange[features.pin_protection])
pin_msg.setVisible(not features.pin_protection)
- passphrase_button.setText(endis[features.passphrase_protection])
+ passphrase_button.setText(endis[bool(passphrase_protection)])
+ passphrase_button.setEnabled(passphrase_protection is not None)
language_label.setText(features.language)
def set_label_enabled():Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.