What changed, and why it matters
This commit is a user-interface polish change: it adds a new green-colored success message method and switches existing success messages from the default text color to green. There is no security-relevant behavior change.
No security action needed; treat as normal UI improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces Page.flash_success(), which calls flash_text() with theme.go_color (green) instead of the default foreground color. It refactors existing success confirmations across settings, file operations, wallet loading, and tamper-code setup to use the new green flash. No logic, cryptography, file handling, or authentication code is modified.
Changed components
src/krux/pages/__init__.pysrc/krux/pages/file_operations.pysrc/krux/pages/fill_flash.pysrc/krux/pages/home_pages/addresses.pysrc/krux/pages/home_pages/bip85.pysrc/krux/pages/home_pages/home.pysrc/krux/pages/home_pages/mnemonic_xor.pysrc/krux/pages/home_pages/wallet_descriptor.pysrc/krux/pages/qr_view.pysrc/krux/pages/settings_page.pyInspect captured patch +33 / −19
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f31ecef..241f25d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ Switch from the pure-Python urtypes and foundation-ur-py packages to the new uUR
### Other Bug Fixes and Improvements
- Improve scan TinySeed and other binary visibility by drawing punches only
+- Added `flash_success` method to standardize green success flashes across confirmation screens
# Changelog 26.04.0 - April 2025
diff --git a/docs/getting-started/features/tamper-detection.en.md b/docs/getting-started/features/tamper-detection.en.md
index 22e8b45..531491e 100644
--- a/docs/getting-started/features/tamper-detection.en.md
+++ b/docs/getting-started/features/tamper-detection.en.md
@@ -39,7 +39,7 @@ When you enable the *TC Flash Hash at Boot* feature, the device will require you
Before being stored in the device’s flash, the *TC Code* is hashed together with the K210 chip’s unique ID and stretched using PBKDF2. This ensures the *TC Code* is not retrievable via a flash dump and can only be brute-forced outside the device if the attacker also has access to the device’s unique ID (UID). By allowing letters, special characters, and running 100k iterations of PBKDF2, brute-forcing the *TC Code* from dumped data becomes more time-consuming and resource-intensive.
### Enhancing Tamper Detection
-After setting the *TC Code*, you are prompted to fill empty flash memory blocks with random entropy from the camera. This process ensures that attackers cannot exploit unused memory space.
+Once the *TC Code* is stored, Krux briefly flashes a green *"Tamper check code set successfully"* confirmation. You are then prompted to fill empty flash memory blocks with random entropy from the camera. This process ensures that attackers cannot exploit unused memory space.
## Tamper Check Flash Hash (TC Flash Hash) - A Tamper Detection Tool
diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py
index 5e954c0..ce97306 100644
--- a/src/krux/pages/__init__.py
+++ b/src/krux/pages/__init__.py
@@ -138,6 +138,12 @@ class Page:
"""Flashes text centered on the display for duration ms"""
self.flash_text(text, theme.error_color)
+ def flash_success(self, text, duration=FLASH_MSG_TIME, highlight_prefix=""):
+ """Flashes success text centered on the display for duration ms"""
+ self.flash_text(
+ text, theme.go_color, duration, highlight_prefix=highlight_prefix
+ )
+
# pylint: disable=too-many-arguments
def capture_from_keypad(
self,
diff --git a/src/krux/pages/file_operations.py b/src/krux/pages/file_operations.py
index e5a94a6..e2f6c6c 100644
--- a/src/krux/pages/file_operations.py
+++ b/src/krux/pages/file_operations.py
@@ -89,7 +89,7 @@ class SaveFile(Page):
sd.write(new_filename, data)
# Show the user the filename
- self.flash_text(
+ self.flash_success(
t("Saved to SD card:") + "\n\n%s" % new_filename,
highlight_prefix=":",
)
diff --git a/src/krux/pages/fill_flash.py b/src/krux/pages/fill_flash.py
index a7dc99c..7cfcc97 100644
--- a/src/krux/pages/fill_flash.py
+++ b/src/krux/pages/fill_flash.py
@@ -122,5 +122,5 @@ class FillFlash(Page):
block_count += 1
self.ctx.camera.stop_sensor()
- self.flash_text(t("Flash filled with camera entropy"))
+ self.flash_success(t("Flash filled with camera entropy"))
return MENU_CONTINUE
diff --git a/src/krux/pages/home_pages/addresses.py b/src/krux/pages/home_pages/addresses.py
index fd1518b..763b7ed 100644
--- a/src/krux/pages/home_pages/addresses.py
+++ b/src/krux/pages/home_pages/addresses.py
@@ -254,7 +254,7 @@ class Addresses(Page):
)
wdt.feed()
- self.flash_text(
+ self.flash_success(
t("Saved to SD card:") + "\n\n%s" % filename,
highlight_prefix=":",
)
diff --git a/src/krux/pages/home_pages/bip85.py b/src/krux/pages/home_pages/bip85.py
index c184024..d1ca636 100644
--- a/src/krux/pages/home_pages/bip85.py
+++ b/src/krux/pages/home_pages/bip85.py
@@ -86,7 +86,7 @@ class Bip85(Page):
from ...wallet import Wallet
self.ctx.wallet = Wallet(key)
- self.flash_text(
+ self.flash_success(
t("%s: loaded!") % key.fingerprint_hex_str(True), highlight_prefix=":"
)
diff --git a/src/krux/pages/home_pages/home.py b/src/krux/pages/home_pages/home.py
index a4fdced..02a48ef 100644
--- a/src/krux/pages/home_pages/home.py
+++ b/src/krux/pages/home_pages/home.py
@@ -311,7 +311,7 @@ class Home(Page):
with open(SDHandler.PATH_STR % psbt_filename, "wb") as f:
# Write PSBT data directly to the file
signer.psbt.write_to(f)
- self.flash_text(
+ self.flash_success(
t("Saved to SD card:") + "\n\n%s" % psbt_filename,
highlight_prefix=":",
)
diff --git a/src/krux/pages/home_pages/mnemonic_xor.py b/src/krux/pages/home_pages/mnemonic_xor.py
index 454f99a..dc7bb44 100644
--- a/src/krux/pages/home_pages/mnemonic_xor.py
+++ b/src/krux/pages/home_pages/mnemonic_xor.py
@@ -171,7 +171,7 @@ class MnemonicXOR(MnemonicLoader):
self.ctx.wallet.key.script_type,
)
self.ctx.wallet = Wallet(xored_key)
- self.flash_text(
+ self.flash_success(
t("%s: loaded!") % xored_fingerprint,
highlight_prefix=":",
)
diff --git a/src/krux/pages/home_pages/wallet_descriptor.py b/src/krux/pages/home_pages/wallet_descriptor.py
index 344e82a..af4cf57 100644
--- a/src/krux/pages/home_pages/wallet_descriptor.py
+++ b/src/krux/pages/home_pages/wallet_descriptor.py
@@ -359,7 +359,7 @@ class WalletDescriptor(Page):
self.display_loading_wallet(wallet)
if self.prompt(t("Load?"), BOTTOM_PROMPT_LINE):
self.ctx.wallet = wallet
- self.flash_text(t("Wallet output descriptor loaded!"))
+ self.flash_success(t("Wallet output descriptor loaded!"))
return MENU_CONTINUE
diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py
index 879837a..6862697 100644
--- a/src/krux/pages/qr_view.py
+++ b/src/krux/pages/qr_view.py
@@ -375,7 +375,7 @@ class SeedQRView(Page):
return
bmp_img.save(SDHandler.PATH_STR % new_filename)
- self.flash_text(
+ self.flash_success(
t("Saved to SD card:") + "\n\n%s" % new_filename,
highlight_prefix=":",
)
diff --git a/src/krux/pages/settings_page.py b/src/krux/pages/settings_page.py
index 16045a9..4bcaa85 100644
--- a/src/krux/pages/settings_page.py
+++ b/src/krux/pages/settings_page.py
@@ -187,7 +187,7 @@ class SettingsPage(Page):
with open(TC_CODE_PATH, "wb") as f:
f.write(secret)
self.ctx.tc_code_enabled = True
- self.flash_text(t("Tamper check code set successfully"))
+ self.flash_success(t("Tamper check code set successfully"))
from .fill_flash import FillFlash
@@ -226,7 +226,7 @@ class SettingsPage(Page):
# Check for SD hot-plug
with SDHandler():
if store.save_settings():
- self.flash_text(
+ self.flash_success(
t("Settings stored on SD card."),
duration=PERSIST_MSG_TIME,
)
@@ -240,7 +240,7 @@ class SettingsPage(Page):
else:
self.ctx.display.clear()
if store.save_settings():
- self.flash_text(
+ self.flash_success(
t("Settings stored internally on flash."),
duration=PERSIST_MSG_TIME,
)
diff --git a/tests/pages/test_page.py b/tests/pages/test_page.py
index 041abd3..3c4319a 100644
--- a/tests/pages/test_page.py
+++ b/tests/pages/test_page.py
@@ -38,7 +38,7 @@ def test_init(mocker, m5stickv, mock_page_cls):
def test_flash_text(mocker, m5stickv, mock_page_cls):
from krux.display import FLASH_MSG_TIME
- from krux.themes import WHITE, RED
+ from krux.themes import WHITE, RED, GREEN
ctx = mock_context(mocker)
mocker.patch("time.ticks_ms", new=lambda: 0)
@@ -57,6 +57,13 @@ def test_flash_text(mocker, m5stickv, mock_page_cls):
"Error", RED, FLASH_MSG_TIME, highlight_prefix=""
)
+ page.flash_success("Done")
+
+ assert ctx.display.flash_text.call_count == 3
+ ctx.display.flash_text.assert_called_with(
+ "Done", GREEN, FLASH_MSG_TIME, highlight_prefix=""
+ )
+
def test_prompt_m5stickv(mocker, m5stickv, mock_page_cls):
from krux.input import BUTTON_ENTER, BUTTON_PAGE
diff --git a/tests/pages/test_settings_page.py b/tests/pages/test_settings_page.py
index 1222640..3e90943 100644
--- a/tests/pages/test_settings_page.py
+++ b/tests/pages/test_settings_page.py
@@ -633,10 +633,10 @@ def test_save_settings_on_sd(amigo, mocker, mocker_sd_card_ok):
ctx = create_ctx(mocker, BTN_SEQUENCE)
settings_page = SettingsPage(ctx)
- settings_page.flash_text = mocker.MagicMock()
+ settings_page.flash_success = mocker.MagicMock()
Settings().persist.location = SD_PATH
settings_page.settings()
- settings_page.flash_text.assert_has_calls(
+ settings_page.flash_success.assert_has_calls(
[
mocker.call("Settings stored on SD card.", duration=2500),
]
@@ -691,13 +691,13 @@ def test_leave_settings_without_changes(amigo, mocker):
for btn_sequence in BTN_SEQUENCES:
ctx = create_ctx(mocker, btn_sequence)
settings_page = SettingsPage(ctx)
- settings_page.flash_text = mocker.MagicMock()
+ settings_page.flash_success = mocker.MagicMock()
settings_page.settings()
persisted_to_flash_call = mocker.call(
"Settings stored internally on flash.", duration=2500
)
assert ctx.input.wait_for_button.call_count == len(btn_sequence)
- assert persisted_to_flash_call not in settings_page.flash_text.call_args_list
+ assert persisted_to_flash_call not in settings_page.flash_success.call_args_list
def test_leave_settings_with_changes(amigo, mocker, mocker_sd_card_ok):
@@ -718,12 +718,12 @@ def test_leave_settings_with_changes(amigo, mocker, mocker_sd_card_ok):
]
ctx = create_ctx(mocker, BTN_SEQUENCE)
settings_page = SettingsPage(ctx)
- settings_page.flash_text = mocker.MagicMock()
+ settings_page.flash_success = mocker.MagicMock()
# Leave settings without changes
settings_page.settings()
assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE)
- settings_page.flash_text.assert_has_calls(
+ settings_page.flash_success.assert_has_calls(
[
mocker.call("Settings stored internally on flash.", duration=2500),
]
Why this scored 15/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.