What changed, and why it matters
This commit is a code cleanup that centralizes touchscreen checks across the Krux firmware. It replaces scattered checks of `self.ctx.input.touch is not None` with a single flag `kboard.has_touchscreen`, and allows touch settings to remain accessible even when the touch input object is temporarily disabled. There is no direct evidence in the commit of a security vulnerability being fixed, but the change touches input-handling logic and could affect behavior if the flag gets out of sync with the actual touch object state.
Treat as a routine refactor with no immediate security action required. Reviewers should verify that `kboard.has_touchscreen` is always kept in sync with the actual presence and state of the `Touch` driver, especially after the new code sets it to `None` when disabling touch at startup. Confirm that exposing touch settings while `input.touch` is disabled does not allow a user to configure an unusable or inconsistent input state.
Security signals we found
Refactoring of input/touch authorization checks
Introduction of a global flag (`kboard.has_touchscreen`) for a hardware capability
Settings page still exposes touch configuration even when touch input object is disabled
No explicit bounds checks, sanitization, or cryptographic changes
Evidence from the diff
The patch refactors touchscreen detection by introducing/using kboard.has_touchscreen instead of checking self.ctx.input.touch is not None in many UI pages. In input.py, the Input class now initializes Touch based on kboard.has_touchscreen, and disables it by setting that flag to None when a touch press is detected at startup. krux_settings.py now checks board.config['krux']['display'].get('touch', False) directly so that touch settings remain visible even if input.touch is disabled. The change is mostly mechanical, but it creates a single source of truth for whether touch is available and changes the semantics of the check in settings.
Changed components
src/krux/input.pysrc/krux/krux_settings.pysrc/krux/pages/__init__.pysrc/krux/pages/device_tests.pysrc/krux/pages/keypads.pysrc/krux/pages/mnemonic_editor.pysrc/krux/pages/qr_view.pysrc/krux/pages/settings_page.pysrc/krux/pages/stack_1248.pysrc/krux/pages/tiny_seed.pytests/pages/test_page.pyInspect captured patch +36 / −35
diff --git a/.pylintrc b/.pylintrc
index b99a6ed..8994c8e 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -257,7 +257,7 @@ dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
-init-import=no
+init-import=yes
# List of qualified module names which can have objects that can redefine
# builtins.
diff --git a/src/krux/input.py b/src/krux/input.py
index 560aa49..77b344c 100644
--- a/src/krux/input.py
+++ b/src/krux/input.py
@@ -97,10 +97,7 @@ class Input:
# This flag, used in selection outlines, is set if buttons are being used
self.buttons_active = True
self.touch = None
- if (
- "touch" in board.config["krux"]["display"]
- and board.config["krux"]["display"]["touch"]
- ):
+ if kboard.has_touchscreen:
from .touch import Touch
self.touch = Touch(
@@ -125,6 +122,7 @@ class Input:
self.page_prev = None
if self.touch and self.touch_value() == PRESSED:
self.touch = None
+ kboard.has_touchscreen = None
def enter_value(self):
"""Intermediary method to pull button ENTER state"""
@@ -149,7 +147,7 @@ class Input:
def touch_value(self):
"""Intermediary method to pull touch state, if touch available"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.value()
return RELEASED
@@ -177,31 +175,31 @@ class Input:
def touch_event(self, validate_position=True):
"""Intermediary method to pull button TOUCH event"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.event(validate_position)
return False
def swipe_right_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.swipe_right_value()
return RELEASED
def swipe_left_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.swipe_left_value()
return RELEASED
def swipe_up_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.swipe_up_value()
return RELEASED
def swipe_down_value(self):
"""Intermediary method to pull touch gesture, if touch available"""
- if self.touch is not None:
+ if kboard.has_touchscreen:
return self.touch.swipe_down_value()
return RELEASED
diff --git a/src/krux/krux_settings.py b/src/krux/krux_settings.py
index 1d0ca7d..9a553f1 100644
--- a/src/krux/krux_settings.py
+++ b/src/krux/krux_settings.py
@@ -375,7 +375,8 @@ class HardwareSettings(SettingsNamespace):
def __init__(self):
self.printer = PrinterSettings()
self.buttons = ButtonsSettings()
- if kboard.has_touchscreen:
+ # Allow touch settings even if input.touch is disabled
+ if board.config["krux"]["display"].get("touch", False):
self.touch = TouchSettings()
if kboard.is_amigo:
self.display = DisplayAmgSettings()
@@ -389,7 +390,8 @@ class HardwareSettings(SettingsNamespace):
"printer": t("Printer"),
}
hardware_menu["buttons"] = t("Buttons")
- if kboard.has_touchscreen:
+ # Allow touch settings even if input.touch is disabled
+ if board.config["krux"]["display"].get("touch", False):
hardware_menu["touchscreen"] = t("Touchscreen")
if kboard.is_amigo:
hardware_menu["display_amg"] = t("Display")
diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py
index 8dd070b..8c560f4 100644
--- a/src/krux/pages/__init__.py
+++ b/src/krux/pages/__init__.py
@@ -21,7 +21,6 @@
# THE SOFTWARE.
import gc
import time
-import board
import lcd
import _thread
from .keypads import Keypad
@@ -46,7 +45,6 @@ from ..display import (
MINIMAL_PADDING,
FLASH_MSG_TIME,
FONT_HEIGHT,
- FONT_WIDTH,
STATUS_BAR_HEIGHT,
BOTTOM_LINE,
)
@@ -103,7 +101,7 @@ class Page:
"""Prompts user for leaving"""
self.ctx.display.clear()
answer = self.prompt(t("Are you sure?"), self.ctx.display.height() // 2)
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
return ESC_KEY if answer else None
@@ -182,7 +180,7 @@ class Page:
if esc_prompt:
if self.esc_prompt() == ESC_KEY:
return ESC_KEY
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.set_regions(
pad.layout.x_keypad_map, pad.layout.y_keypad_map
)
@@ -191,7 +189,7 @@ class Page:
elif pad.cur_key_index == pad.go_index:
break
elif pad.cur_key_index == pad.more_index:
- swipeable = self.ctx.input.touch is not None
+ swipeable = kboard.has_touchscreen
if swipeable and swipe_has_not_been_used:
show_swipe_hint = True
pad.next_keyset()
@@ -212,7 +210,7 @@ class Page:
if btn in (SWIPE_RIGHT, SWIPE_LEFT):
swipe_has_not_been_used = False
pad.navigate(btn)
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
return buffer
@@ -325,7 +323,7 @@ class Page:
qr_foreground = None
extra_debounce_flag = True
elif btn in PROCEED:
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.buttons_active = False
done = True
@@ -414,7 +412,7 @@ class Page:
self.y_keypad_map.append(y_key_map)
y_key_map += 4 * FONT_HEIGHT
self.y_keypad_map.append(min(y_key_map, self.ctx.display.height()))
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.set_regions(self.x_keypad_map, self.y_keypad_map)
btn = None
answer = True
@@ -450,7 +448,7 @@ class Page:
2 * FONT_HEIGHT - 2,
theme.no_esc_color,
)
- elif self.ctx.input.touch is not None:
+ elif kboard.has_touchscreen:
self.ctx.display.draw_vline(
self.ctx.display.width() // 2,
self.y_keypad_map[0] + FONT_HEIGHT,
@@ -729,7 +727,7 @@ class Menu:
)
else:
self.ctx.display.clear()
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self._draw_touch_menu(selected_item_index)
else:
self._draw_menu(selected_item_index)
@@ -742,7 +740,7 @@ class Menu:
start_from_submenu = False
else:
btn = self._get_btn_input()
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
if btn == BUTTON_TOUCH:
selected_item_index = self.ctx.input.touch.current_index()
btn = BUTTON_ENTER
diff --git a/src/krux/pages/device_tests.py b/src/krux/pages/device_tests.py
index 373dd78..72b036b 100644
--- a/src/krux/pages/device_tests.py
+++ b/src/krux/pages/device_tests.py
@@ -419,7 +419,7 @@ class DeviceTests(Page):
if not interactive:
return "Cannot test non-interactively"
- if self.ctx.input.touch is None:
+ if not kboard.has_touchscreen:
return "Touch not available"
results = []
diff --git a/src/krux/pages/keypads.py b/src/krux/pages/keypads.py
index 86f2604..6dba0bb 100644
--- a/src/krux/pages/keypads.py
+++ b/src/krux/pages/keypads.py
@@ -37,6 +37,7 @@ from ..input import (
)
from ..buttons import PRESSED
from ..display import DEFAULT_PADDING, MINIMAL_PADDING, FONT_HEIGHT, FONT_WIDTH
+from ..kboard import kboard
FIXED_KEYS = 3 # 'More' key only appears when there are multiple keysets.
@@ -67,7 +68,7 @@ class KeypadLayout:
for x in range(1, self.width):
self.x_keypad_map.append(x * key_h_spacing + MINIMAL_PADDING)
self.x_keypad_map.append(ctx.display.width())
- if ctx.input.touch is not None:
+ if kboard.has_touchscreen:
ctx.input.touch.set_regions(self.x_keypad_map, self.y_keypad_map)
@@ -183,7 +184,7 @@ class Keypad:
key_offset_x, offset_y, key, theme.disabled_color
)
else:
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.display.outline(
offset_x + 1,
y + 1,
@@ -201,7 +202,7 @@ class Keypad:
key_index == self.cur_key_index
and self.ctx.input.buttons_active
):
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.display.outline(
offset_x + 1,
y + 1,
diff --git a/src/krux/pages/mnemonic_editor.py b/src/krux/pages/mnemonic_editor.py
index c3327a8..b54a8b4 100644
--- a/src/krux/pages/mnemonic_editor.py
+++ b/src/krux/pages/mnemonic_editor.py
@@ -180,7 +180,7 @@ class MnemonicEditor(Page):
word_v_padding = self.ctx.display.height() * 3 // 4
word_v_padding //= 12
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
self.ctx.input.touch.x_regions.append(0)
self.ctx.input.touch.x_regions.append(self.ctx.display.width() // 2)
diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py
index caa7208..564aba6 100644
--- a/src/krux/pages/qr_view.py
+++ b/src/krux/pages/qr_view.py
@@ -513,7 +513,7 @@ class SeedQRView(Page):
label = self.title
else:
label = ""
- if transcript_tools and self.ctx.input.touch is not None:
+ if transcript_tools and kboard.has_touchscreen:
label += "\n" + t("Swipe to change mode")
mode = 0
while True:
diff --git a/src/krux/pages/settings_page.py b/src/krux/pages/settings_page.py
index 20cd559..16045a9 100644
--- a/src/krux/pages/settings_page.py
+++ b/src/krux/pages/settings_page.py
@@ -83,7 +83,7 @@ class SettingsPage(Page):
def _draw_settings_pad(self):
"""Draws buttons to change settings with touch"""
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
offset_y = self.ctx.display.height() * 2 // 3
self.ctx.input.touch.add_y_delimiter(offset_y)
@@ -315,7 +315,7 @@ class SettingsPage(Page):
"""Handler for the 'Back' on touch settings screen"""
# Update touch detection threshold
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.touch_driver.threshold(
Settings().hardware.touch.threshold
)
diff --git a/src/krux/pages/stack_1248.py b/src/krux/pages/stack_1248.py
index ebeab1b..c1dd253 100644
--- a/src/krux/pages/stack_1248.py
+++ b/src/krux/pages/stack_1248.py
@@ -320,7 +320,7 @@ class Stackbit(Page):
theme.go_color,
)
# print border around buttons only on touch devices
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.display.draw_line(
x_offset,
y_offset,
@@ -367,7 +367,7 @@ class Stackbit(Page):
def _map_keys_array(self):
"""Maps an array of regions for keys to be placed in"""
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.clear_regions()
x_region = self.x_offset + self.x_pad
for _ in range(8):
diff --git a/src/krux/pages/tiny_seed.py b/src/krux/pages/tiny_seed.py
index df13807..0c9cee4 100644
--- a/src/krux/pages/tiny_seed.py
+++ b/src/krux/pages/tiny_seed.py
@@ -257,7 +257,7 @@ class TinySeed(Page):
def _map_keys_array(self):
"""Maps an array of regions for keys to be placed in"""
- if self.ctx.input.touch is not None:
+ if kboard.has_touchscreen:
self.ctx.input.touch.x_regions = [
self.x_offset + i * self.x_pad for i in range(13)
]
diff --git a/tests/pages/test_page.py b/tests/pages/test_page.py
index 78bac78..f57d1aa 100644
--- a/tests/pages/test_page.py
+++ b/tests/pages/test_page.py
@@ -184,6 +184,7 @@ def get_frame_titles_resulting_from_input(
):
from krux.input import BUTTON_TOUCH, SWIPE_RIGHT, BUTTON_PAGE, BUTTON_ENTER
from krux.pages.keypads import Keypad
+ from krux.kboard import kboard
def get_button_seq(key_index):
return [*([BUTTON_PAGE] * (key_index - 1))] + [BUTTON_ENTER]
@@ -220,6 +221,7 @@ def get_frame_titles_resulting_from_input(
ctx = create_ctx(mocker, button_seq, None, None, touch_seq)
if not has_touch:
ctx.input.touch = None
+ kboard.has_touchscreen = False
page = mock_page_cls(ctx)
captured = page.capture_from_keypad(title, keysets)
frame_titles = [
Why this scored 27/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.