fix(delizia): `show_properties` layout consistency
What changed, and why it matters
This commit changes how the Trezor hardware wallet displays a single property on screen. Previously, passing one property as a list used a different screen layout than passing the same property as a plain string. Now both cases use the same ConfirmValue screen. This is a UI consistency fix with no obvious security impact, though any UI change can in principle affect what the user sees and approves.
No security action required. Treat as a normal UI consistency patch. If desired, verify that the single-property ConfirmValue layout presents the same information as the previous ShowInfoParams layout to ensure users still see all expected fields.
Security signals we found
UI layout normalization
No input validation or cryptographic changes
No privilege escalation or memory safety issue evident
Potential minor UX consistency impact on user confirmation screens
Evidence from the diff
The show_properties method in the Delizia UI layout now normalizes a single-element property list to use ConfirmValue instead of ShowInfoParams. It collects properties into a heapless Vec, and if exactly one item is present, renders it with ConfirmValue::new(...).with_subtitle(...).with_cancel_button().with_chunkify(is_data).with_text_mono(true). Otherwise it falls back to the existing multi-property ShowInfoParams flow. The commit does not change validation, parsing, or authorization logic.
Changed components
core/embed/rust/src/ui/layout_delizia/ui_firmware.rsTrezor firmware Delizia UIshow_properties flowInspect captured patch +23 / −5
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index 95d72952..b2b2cce7 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -34,6 +34,7 @@ use crate::{
ModelUI,
},
};
+use heapless::Vec;
use super::{
component::{
@@ -1180,19 +1181,36 @@ impl FirmwareUI for UIDelizia {
return flow::util::single_page(layout.map(|_| Some(FlowMsg::Confirmed)));
}
- let mut params = ShowInfoParams::new(title).with_cancel_button();
+ let mut items: Vec<(TString<'static>, TString<'static>, bool), 4> = Vec::new();
for property in IterBuf::new().try_iterate(value)? {
- let [header, text, _is_data]: [Obj; 3] = util::iter_into_array(property)?;
+ let [header, text, is_data]: [Obj; 3] = util::iter_into_array(property)?;
let header = header
.try_into_option::<TString>()?
.unwrap_or_else(TString::empty);
let text = text
.try_into_option::<TString>()?
.unwrap_or_else(TString::empty);
- params = unwrap!(params.add(header, text));
+ let is_data = is_data.try_into_option::<bool>()?.unwrap_or(false);
+ unwrap!(items.push((header, text, is_data)));
+ }
+
+ if items.len() == 1 {
+ let (header, value, is_data) = unwrap!(items.pop());
+ let confirm = ConfirmValue::new(title, value.into(), None)
+ .with_subtitle(if header != title { Some(header) } else { None })
+ .with_cancel_button()
+ .with_chunkify(is_data)
+ .with_text_mono(true);
+ let layout = confirm.into_layout()?;
+ flow::util::single_page(layout.map(|_| Some(FlowMsg::Confirmed)))
+ } else {
+ let mut params = ShowInfoParams::new(title).with_cancel_button();
+ for (header, text, _is_data) in items {
+ params = unwrap!(params.add(header, text));
+ }
+ let layout = params.into_layout()?;
+ flow::util::single_page(layout.map(|_| Some(FlowMsg::Confirmed)))
}
- let layout = params.into_layout()?;
- flow::util::single_page(layout.map(|_| Some(FlowMsg::Confirmed)))
}
fn show_share_words(
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.