fix(core): make sure `SwipeFlow` sends `ButtonRequest` on attach
What changed, and why it matters
This is a small UI bug fix in the Trezor hardware wallet firmware. It ensures that when a user swipes between on-screen pages, the device correctly sends a 'ButtonRequest' event at the moment a new page is attached. Without this, automated tests for the device recovery process failed on the newer 'Delizia' screen layout because the expected button request was missing or duplicated. There is no indication this allows theft of funds, bypass of security, or user data exposure.
No security action required. Treat as a normal functional/UI fix. If reviewing for release, verify that recovery-device tests pass on Delizia and that no other flows depend on the old ButtonRequest timing.
Security signals we found
Missing expected ButtonRequest in UI flow could break host-side confirmation sequencing
Fix is localized to UI event propagation, not cryptographic or authorization logic
Change triggered by automated test failure, not by a security report
Evidence from the diff
In core/embed/rust/src/ui/flow/swipe.rs, the SwipeFlow event loop now mutates the local event variable to Event::Attach(AttachType::Swipe(dir)) before passing it to the current page’s event handler. Previously, the code called event() with the Attach event directly but left the local event as SwipeEvent::End. Because the surrounding code likely emits ButtonRequest based on the event value returned/observed downstream, the attach-time ButtonRequest was not being produced. The test helper in tests/input_flows_helpers.py is updated to assert the expected ‘show_shares’ ButtonRequest immediately after navigation and to re-assert it after each swipe on Delizia, matching the corrected behavior.
Changed components
core/embed/rust/src/ui/flow/swipe.rstests/input_flows_helpers.pyTrezor Safe 5 (Delizia layout) recovery-device UI flowInspect captured patch +6 / −5
diff --git a/core/embed/rust/src/ui/flow/swipe.rs b/core/embed/rust/src/ui/flow/swipe.rs
index 314eecc82..91e990346 100644
--- a/core/embed/rust/src/ui/flow/swipe.rs
+++ b/core/embed/rust/src/ui/flow/swipe.rs
@@ -194,7 +194,7 @@ impl SwipeFlow {
let mut attach = false;
- let event = if self.allow_swipe {
+ let mut event = if self.allow_swipe {
let page = self.current_page();
let pager = page.get_pager();
let config = page.get_swipe_config().with_pager(pager);
@@ -232,8 +232,8 @@ impl SwipeFlow {
// swipe end.
if attach {
if let Event::Swipe(SwipeEvent::End(dir)) = event {
- self.current_page_mut()
- .event(ctx, Event::Attach(AttachType::Swipe(dir)));
+ event = Event::Attach(AttachType::Swipe(dir));
+ self.current_page_mut().event(ctx, event);
}
}
diff --git a/tests/input_flows_helpers.py b/tests/input_flows_helpers.py
index ba7a4e320..26b797dc4 100644
--- a/tests/input_flows_helpers.py
+++ b/tests/input_flows_helpers.py
@@ -402,16 +402,17 @@ class RecoveryFlow:
self.debug.synchronize_at("VerticalMenu")
self.debug.button_actions.navigate_to_menu_item(0)
br = yield
+ assert br.name == "show_shares"
+ assert br.code == B.Other
# Scroll through remaining share pages
assert br.pages is not None
for _ in range(br.pages - 1):
if self.client.layout_type is LayoutType.Delizia:
self.debug.swipe_up()
+ assert br == (yield)
elif self.client.layout_type is LayoutType.Eckhart:
self.debug.click(self.debug.screen_buttons.ok())
- assert br.name == "show_shares"
- assert br.code == B.Other
# Getting back to the homepage
self.debug.click(self.debug.screen_buttons.menu())
self.debug.click(self.debug.screen_buttons.menu())
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.