feat(ui): assert property list is never empty.
What changed, and why it matters
This commit adds a runtime check (assert) that ensures a UI function for confirming on-screen properties is never called with an empty list. It also changes the expected input type from a generic iterable to a sequence and converts a couple of generator expressions into lists. The commit message explicitly says it will not affect production builds, because assertions are typically stripped from production firmware. There is no direct evidence this fixes an active security bug; it appears to be a defensive hardening measure for development/debug builds.
No immediate action required. Treat as routine defensive hardening. If empty property lists are considered a security concern in production, replace the `assert` with a runtime check that is not stripped in optimized builds, and add a regression test.
Security signals we found
Defensive assertion added to UI confirmation path
Type narrowed from Iterable to Sequence to support emptiness check and repeated iteration
Caller sites converted from generator/tuple to list to satisfy Sequence contract
Commit message states 'Will not affect the production builds' and '[no changelog]'
Evidence from the diff
The confirm_properties helper across four UI layout backends (bolt, caesar, delizia, eckhart) now asserts that the props argument is non-empty. The type hint changes from Iterable[PropertyType] to Sequence[PropertyType], and callers in EOS and Solana apps that previously passed generators/tuples now pass lists. The assertion prevents an empty confirmation page from being rendered, which could otherwise lead to a confusing or blank user-approval screen. Because Python assert statements are removed in optimized/production builds, this check only affects debug/development builds unless the project ships with assertions enabled.
Changed components
core/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pycore/src/apps/eos/actions/layout.pycore/src/apps/solana/layout.pyInspect captured patch +17 / −9
### core/src/apps/eos/actions/layout.py
@@ -7,7 +7,7 @@
from ..helpers import eos_asset_to_string, eos_name_to_string
if TYPE_CHECKING:
- from collections.abc import Iterable
+ from collections.abc import Sequence
from trezor.messages import (
EosActionBuyRam,
@@ -38,7 +38,7 @@
async def _confirm_properties(
br_name: str,
title: str,
- props: Iterable[PropertyType],
+ props: Sequence[PropertyType],
) -> None:
await confirm_properties(
br_name,
@@ -145,10 +145,10 @@ async def confirm_action_voteproducer(msg: EosActionVoteProducer) -> None:
await _confirm_properties(
"confirm_voteproducer",
TR.eos__vote_for_producers,
- (
+ [
(None, f"{wi:2d}. {eos_name_to_string(producer)}", None)
for wi, producer in enumerate(producers, 1)
- ),
+ ],
)
else:
### core/src/apps/solana/layout.py
@@ -267,7 +267,7 @@ async def confirm_unsupported_instruction_details(
await confirm_properties(
"accounts",
title,
- maybe_with_colon(accounts),
+ list(maybe_with_colon(accounts)),
)
### core/src/trezor/ui/layouts/bolt/__init__.py
@@ -884,13 +884,15 @@ async def confirm_value(
async def confirm_properties(
br_name: str,
title: str,
- props: Iterable[PropertyType],
+ props: Sequence[PropertyType],
subtitle: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
verb: str | None = None,
) -> None:
+ assert props
+
items = with_colon(
(
prop[0],
### core/src/trezor/ui/layouts/caesar/__init__.py
@@ -897,12 +897,14 @@ def confirm_text(
async def confirm_properties(
br_name: str,
title: str,
- props: Iterable[PropertyType], # TODO: replace with StrPropertyType
+ props: Sequence[PropertyType], # TODO: replace with StrPropertyType
subtitle: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
verb: str | None = None,
) -> None:
+
+ assert props
from ..properties import with_colon
items = with_colon(
### core/src/trezor/ui/layouts/delizia/__init__.py
@@ -869,13 +869,15 @@ async def confirm_value(
async def confirm_properties(
br_name: str,
title: str,
- props: Iterable[PropertyType],
+ props: Sequence[PropertyType],
subtitle: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
verb: str | None = None,
) -> None:
+ assert props
+
with trezorui_api.confirm_properties(
title=title,
subtitle=subtitle,
### core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -880,12 +880,14 @@ async def confirm_value(
async def confirm_properties(
br_name: str,
title: str,
- props: Iterable[PropertyType],
+ props: Sequence[PropertyType],
subtitle: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
verb: str | None = None,
) -> None:
+
+ assert props
from trezor.ui.layouts.menu import Menu, cancel_leaf, confirm_with_menu
menu = Menu([cancel_leaf(TR.buttons__cancel)])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.