From 77f576c50c8a49d8c7466f9edfa33ce1fc25f86c Mon Sep 17 00:00:00 2001 From: scgbckbone Date: Tue, 30 Apr 2024 22:11:21 +0200 Subject: [PATCH] bugfix: Q seed entry restrict allowed keys after final word inserted --- releases/ChangeLog.md | 1 + shared/ux_q1.py | 16 ++++++++++------ testing/test_ux.py | 23 +++++++++++++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/releases/ChangeLog.md b/releases/ChangeLog.md index 020d4cce..62ea4947 100644 --- a/releases/ChangeLog.md +++ b/releases/ChangeLog.md @@ -60,6 +60,7 @@ This lists the changes in the most recent firmware, for each hardware platform. - Bugfix: Battery idle timeout also considers last progress bar update - Bugfix: Allow `Send Password` (keystrokes) of capital letters of alphabet - Bugfix: Pressing SYM+SHIFT was toggling CAPS continuously. Now toggles once only. +- Bugfix: Restrict keys that can be pressed during seed entry after final word inserted # Release History diff --git a/shared/ux_q1.py b/shared/ux_q1.py index 85d20588..712a42cf 100644 --- a/shared/ux_q1.py +++ b/shared/ux_q1.py @@ -604,7 +604,7 @@ async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): assert num_words and prompt and done_cb - words = ['' for i in range(num_words)] + words = ['' for _ in range(num_words)] dis.clear() dis.text(None, 0, prompt, invert=1) @@ -645,11 +645,12 @@ async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): ch = await press.wait() commit = False + final = (word_num == num_words) if ch == KEY_ENTER: - if word_num == num_words: + if final: break commit = True - elif ch == KEY_DELETE or ch == KEY_LEFT: + elif (ch == KEY_DELETE) or (ch == KEY_LEFT): # delete last char if len(value) > 0: value = value[:-1] @@ -668,8 +669,10 @@ async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): dis.restore_state(tmp) continue return None - - elif ch in { ' ', KEY_TAB, KEY_DOWN, KEY_RIGHT }: + elif final: + # below options not allowed if all words already provided + continue + elif ch in {' ', KEY_TAB, KEY_DOWN, KEY_RIGHT}: # re-consider if word done, like "act" and other 3-letter cases commit = True elif ch.isalpha(): @@ -677,7 +680,7 @@ async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): else: continue - if has_checksum and word_num == num_words-1 and (len(value) >= 1 or commit): + if has_checksum and (word_num == num_words-1) and ((len(value) >= 1) or commit): assert last_words if value not in last_words: maybe = [i for i in last_words if i.startswith(value)] @@ -696,6 +699,7 @@ async def seed_word_entry(prompt, num_words, has_checksum=True, done_cb=None): nextchars = ''.join(sorted(set(i[len(value)] for i in maybe))) err_msg = 'Next key: ' + nextchars continue + if value in last_words: dis.text(x, y, '%-8s' % value) words[word_num] = value diff --git a/testing/test_ux.py b/testing/test_ux.py index 175f0e0c..20aa66b4 100644 --- a/testing/test_ux.py +++ b/testing/test_ux.py @@ -66,7 +66,7 @@ def test_home_menu(cap_menu, cap_story, cap_screen, need_keypress, reset_seed_wo @pytest.fixture def word_menu_entry(cap_menu, pick_menu_item, is_q1, do_keypresses, cap_screen): - def doit(words, has_checksum=True): + def doit(words, has_checksum=True, q_accept=True): if is_q1: # easier for us on Q, but have to anticipate the autocomplete for n, w in enumerate(words, start=1): @@ -106,7 +106,9 @@ def word_menu_entry(cap_menu, pick_menu_item, is_q1, do_keypresses, cap_screen): assert 'Valid words' in cap_scr else: assert 'Press ENTER if all done' in cap_scr - do_keypresses('\r') + + if q_accept: + do_keypresses('\r') return # do the massive drilling-down to pick a specific pass phrase @@ -804,6 +806,23 @@ def test_bip39_pw_signing_xfp_ux(pick_menu_item, press_select, cap_story, enter_ reset_seed_words() # for subsequent tests +def test_q1_seed_word_entry_bug(word_menu_entry, unit_test, pick_menu_item, + is_q1, do_keypresses, press_select, expect_ftux): + # internal/issues/750 + if not is_q1: + raise pytest.skip("Q only") + + unit_test('devtest/clear_seed.py') + pick_menu_item('Import Existing') + pick_menu_item('24 Words') + sw = ["abandon"] * 23 + sw += ["art"] + word_menu_entry(sw, q_accept=False) + do_keypresses("art") + # now we we are yikes if bug not fixed + press_select() + expect_ftux() + @pytest.mark.onetime def test_dump_menutree(sim_execfile): # saves to ../unix/work/menudump.txt