fix(python): support older releases without `Features.capabilities`
What changed, and why it matters
This is a small bug-fix in Trezor's Python library. Older Trezor firmware did not advertise which features it supports via a 'capabilities' field, so the library was incorrectly throwing errors when connecting to those older devices. The change makes the library skip capability checks when the field is missing, and it unifies the error handling. It does not appear to be a security fix.
No security action required; treat as a normal compatibility fix. If reviewing, verify that skipping capability checks on old firmware does not re-enable unsafe behavior that was previously blocked by other means.
Security signals we found
No security-relevant signal: change is a compatibility fix for missing protobuf field
No change to cryptographic, authentication, or authorization logic
New exception type is informational only
Evidence from the diff
The commit adds a check_capability() helper in client.py that returns early if self.features.capabilities is falsy (i.e., older firmware that lacks the field). It replaces two inline capability checks for Cardano and on-device passphrase entry with calls to this helper, and introduces a new MissingCapability exception class. The change is purely defensive/compatibility-oriented and does not alter access-control semantics for firmware that does expose capabilities.
Changed components
python/src/trezorlib/client.pypython/src/trezorlib/exceptions.pyInspect captured patch +20 / −12
diff --git a/python/src/trezorlib/client.py b/python/src/trezorlib/client.py
index d6b25ed8..dc7882b1 100644
--- a/python/src/trezorlib/client.py
+++ b/python/src/trezorlib/client.py
@@ -313,6 +313,14 @@ class TrezorClient(t.Generic[SessionType], metaclass=ABCMeta):
def is_connected(self) -> bool:
return True
+ def check_capability(self, capability: messages.Capability) -> None:
+ if not self.features.capabilities:
+ # Older firmware didn't support `Features.capabilities`
+ return
+
+ if capability not in self.features.capabilities:
+ raise exceptions.MissingCapability(self.version, capability)
+
def get_session(
self,
passphrase: str | PassphraseSetting | None = PassphraseSetting.STANDARD_WALLET,
@@ -342,18 +350,11 @@ class TrezorClient(t.Generic[SessionType], metaclass=ABCMeta):
self.connect()
self.check_firmware_version()
- if (
- derive_cardano
- and messages.Capability.Cardano not in self.features.capabilities
- ):
- raise exceptions.TrezorException("Cardano is not available on this device.")
- if (
- passphrase is PassphraseSetting.ON_DEVICE
- and messages.Capability.PassphraseEntry not in self.features.capabilities
- ):
- raise exceptions.PassphraseError(
- "Passphrase entry is not available on this device."
- )
+ if derive_cardano:
+ self.check_capability(messages.Capability.Cardano)
+
+ if passphrase is PassphraseSetting.ON_DEVICE:
+ self.check_capability(messages.Capability.PassphraseEntry)
if isinstance(passphrase, str):
passphrase = unicodedata.normalize("NFKD", passphrase)
diff --git a/python/src/trezorlib/exceptions.py b/python/src/trezorlib/exceptions.py
index a5ff4ceb..799c4ee8 100644
--- a/python/src/trezorlib/exceptions.py
+++ b/python/src/trezorlib/exceptions.py
@@ -159,3 +159,10 @@ class StateMismatchError(TrezorException):
Raised when the caller invokes a function that does not match the current
state of a flow.
"""
+
+
+class MissingCapability(TrezorException):
+ """Current firmware doesn't have the required capability.
+
+ Raised when the caller invokes a function that is not supported.
+ """
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.