chore(core): disallow showing dangerous warning with no header
What changed, and why it matters
This commit adds a safety check to three Trezor device user-interface layouts (Bolt, Caesar, Eckhart). It prevents a 'dangerous' warning screen from being shown without a title/header. The change is defensive: previously the unused `_danger` flag was ignored, so a caller could accidentally or maliciously present a high-risk warning with no context. Now such calls fail with an error. The commit message frames this as a chore, not a security fix, and no changelog entry was added.
Treat as a minor hardening improvement. Review all call sites of `show_warning` to confirm none rely on passing `danger=true` with `title=None`; if any do, they will now error. Consider adding a changelog or release note describing the behavioral change, since it affects runtime error behavior of a UI API.
Security signals we found
Defensive input validation added to UI warning path
Previously ignored `_danger` parameter now enforced
Prevents dangerous warning from being rendered without context/header
Mirrors prior hardening in another layout (Delizia)
No changelog entry despite security-relevant behavior change
Evidence from the diff
The patch modifies show_warning in ui_firmware.rs for the Bolt, Caesar, and Eckhart layouts. It renames the previously ignored _danger parameter to danger and adds a guard: if danger is true and title is None, the function returns Error::ValueError(c"Non-empty title is required"). This mirrors an earlier change for the Delizia layout (commit a6090dce50). The intent is to enforce that high-risk/danger warnings always carry a descriptive header, reducing the chance of a user approving an ambiguous critical action.
Changed components
core/embed/rust/src/ui/layout_bolt/ui_firmware.rscore/embed/rust/src/ui/layout_caesar/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/ui_firmware.rsTrezor firmware UI warning rendering for Bolt, Caesar, Eckhart layoutsInspect captured patch +18 / −4
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index 3bf2b78e..021bb180 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -1276,7 +1276,7 @@ impl FirmwareUI for UIBolt {
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
- _danger: bool,
+ danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
let icon = BlendedImage::new(
theme::IMAGE_BG_OCTAGON,
@@ -1285,6 +1285,10 @@ impl FirmwareUI for UIBolt {
theme::FG,
theme::BG,
);
+ if danger && title.is_none() {
+ // Disallow showing "dangerous" warning with no header.
+ return Err(Error::ValueError(c"Non-empty title is required"));
+ }
new_show_modal(
title.unwrap_or(TString::empty()),
value,
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index de882e2b..b6df6b29 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -1366,13 +1366,17 @@ impl FirmwareUI for UICaesar {
}
fn show_warning(
- _title: Option<TString<'static>>,
+ title: Option<TString<'static>>,
button: TString<'static>,
value: TString<'static>,
description: TString<'static>,
_allow_cancel: bool,
- _danger: bool,
+ danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
+ if danger && title.is_none() {
+ // Disallow showing "dangerous" warning with no header.
+ return Err(Error::ValueError(c"Non-empty title is required"));
+ }
let get_page = move |page_index| {
assert!(page_index == 0);
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index 0252b100..73e26ce6 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -1576,7 +1576,13 @@ impl FirmwareUI for UIEckhart {
};
let screen = TextScreen::new(paragraphs).with_action_bar(action_bar);
let screen = match title {
- None => screen,
+ None => {
+ if danger {
+ // Disallow showing "dangerous" warning with no header.
+ return Err(Error::ValueError(c"Non-empty title is required"));
+ }
+ screen
+ }
Some(title) => screen.with_header(
Header::new(title)
.with_icon(theme::ICON_INFO, color)
Why this scored 38/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.