fix(solana): avoid re-confirmation in staking flows
What changed, and why it matters
This change fixes a logic bug in how Trezor handles Solana staking transactions. Previously, the device could ask the user to confirm a staking detail before it had fully verified that the transaction matched a safe, predefined pattern. The fix moves that confirmation check to after all safety checks are complete, so the device only asks for confirmation when it is confident the transaction is a known, legitimate staking flow. This prevents a potentially malicious or malformed transaction from tricking the user into confirming something risky.
Review the Solana predefined transaction module for other confirmation ordering issues, ensure all confirmation prompts occur only after complete transaction validation, and consider adding regression tests that verify no UI prompts are emitted for malformed or non-matching staking transactions.
Security signals we found
UI confirmation ordering bug in transaction validation
Possible user confusion or premature approval in Solana staking flows
Validation gate bypass risk if confirmation is requested before full matching
No changelog entry provided
Evidence from the diff
In core/src/apps/solana/predefined_transaction.py, the code that matches a Solana stake-create-and-delegate transaction previously called confirm_stake_withdrawer() inside a series of validation checks, before all conditions for the predefined flow were verified. The patch moves the withdrawer confirmation to occur only after every structural and signer-consistency check has passed. This avoids a re-confirmation or premature user prompt in staking flows and ensures confirmation UI is only shown for transactions that fully qualify for the predefined, safer path.
Changed components
core/src/apps/solana/predefined_transaction.pySolana staking (create-and-delegate) predefined transaction flowTrezor device confirmation UI for Solana stake withdrawerInspect captured patch +3 / −2
### core/src/apps/solana/predefined_transaction.py
@@ -309,8 +309,6 @@ def _match_instructions(*expected_types: type[Instruction]) -> bool:
return False
if signer_public_key != create.base:
return False
- if signer_public_key != init.withdrawer:
- await confirm_stake_withdrawer(init.withdrawer, chunkify)
if signer_public_key != init.staker:
return False
if signer_public_key != delegate.stake_authority[0]:
@@ -332,6 +330,9 @@ def _match_instructions(*expected_types: type[Instruction]) -> bool:
):
return False
+ if signer_public_key != init.withdrawer:
+ await confirm_stake_withdrawer(init.withdrawer, chunkify)
+
await confirm_stake_transaction(
fee=fee,
signer_path=signer_path,Why this scored 35/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.