diff --git a/shared/actions.py b/shared/actions.py index 6910aba4..dfb371cc 100644 --- a/shared/actions.py +++ b/shared/actions.py @@ -1397,7 +1397,7 @@ async def import_xprv(_1, _2, item): else: # only get here if NFC was not chosen # pick a likely-looking file. - fn = await file_picker(suffix='txt', min_size=50, max_size=2000, taster=contains_xprv, + fn = await file_picker(suffix='.txt', min_size=50, max_size=2000, taster=contains_xprv, none_msg="Must contain " + label + ".", **choice) if not fn: return @@ -1728,6 +1728,8 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, # - escape: allow these chars to skip picking process # - slot_b: None=>pick slot w/ card in it, or A if both. # - allow_batch: adds an "all of the above" choice: ("menu label", menu_handler) + # suffix argument MUST contain the dot (.), if list of suffixes - all MUST contain the dot + if choices is None: choices = [] @@ -1744,6 +1746,8 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, if suffix: if not isinstance(suffix, list): suffix = [suffix] + + assert all(s[0] == "." for s in suffix) if not any([fn.lower().endswith(s) for s in suffix]): continue @@ -1793,7 +1797,7 @@ async def file_picker(suffix=None, min_size=1, max_size=1000000, taster=None, if none_msg: msg += none_msg if suffix: - msg += '\n\nThe filename must end in %r. ' % suffix + msg += '\n\nThe filename must end in: %s' % ",".join(["*" + s for s in suffix]) msg += '\n\nMaybe insert (another) SD card and try again?' @@ -1873,7 +1877,7 @@ async def _batch_sign(choices=None): return assert isinstance(picked, dict) - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt, **picked) if not choices: @@ -1911,7 +1915,7 @@ async def ready2sign(*a): opt = {} # just check if we have candidates, no UI - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt) if pa.tmp_value: @@ -1938,7 +1942,7 @@ from your desktop wallet software or command line tools.''' title=title) if isinstance(picked, dict): opt = picked # reset options to what was chosen by user - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt, **opt) if not choices: @@ -1980,7 +1984,7 @@ async def sign_message_on_sd(*a): # min 1 line max 3 lines return 1 <= len(lines) <= 3 - fn = await file_picker(suffix=['txt', "json"], min_size=2, max_size=500, taster=is_signable, + fn = await file_picker(suffix=['.txt', ".json"], min_size=2, max_size=500, taster=is_signable, none_msg=('Must be txt file with one msg line, optionally ' 'followed by a subkey derivation path on a second line ' 'and/or address format on third line. JSON msg signing ' diff --git a/shared/ccc.py b/shared/ccc.py index 641aa0db..bdcca003 100644 --- a/shared/ccc.py +++ b/shared/ccc.py @@ -587,7 +587,7 @@ class SPAddrWhitelist(MenuSystem): pat = re.compile(r'[^A-Za-z0-9]') # pick a likely-looking file: just looking at size and extension - fn = await file_picker(suffix=['csv', 'txt'], + fn = await file_picker(suffix=['.csv', '.txt'], min_size=20, max_size=20000, none_msg="Must contain payment addresses", **choice) diff --git a/shared/tapsigner.py b/shared/tapsigner.py index e8def94c..9bce68d4 100644 --- a/shared/tapsigner.py +++ b/shared/tapsigner.py @@ -67,7 +67,7 @@ async def import_tapsigner_backup_file(_1, _2, item): continue break else: - fn = await file_picker(suffix="aes", min_size=100, max_size=160, **choice) + fn = await file_picker(suffix=".aes", min_size=100, max_size=160, **choice) if not fn: return origin += (" (%s)" % fn) try: diff --git a/shared/teleport.py b/shared/teleport.py index 37f8db3b..05941892 100644 --- a/shared/teleport.py +++ b/shared/teleport.py @@ -734,7 +734,7 @@ async def kt_send_file_psbt(*a): picked = await import_export_prompt("PSBT", is_import=True, no_nfc=True, no_qr=True) if picked == KEY_CANCEL: return - choices = await file_picker(suffix='psbt', min_size=50, ux=False, + choices = await file_picker(suffix='.psbt', min_size=50, ux=False, max_size=MAX_TXN_LEN, taster=is_psbt, **picked) if not choices: # error msg already shown diff --git a/testing/test_ux.py b/testing/test_ux.py index afb1eeac..bd206cd6 100644 --- a/testing/test_ux.py +++ b/testing/test_ux.py @@ -1155,6 +1155,47 @@ def test_q1_24_8char_words(set_seed_words, is_q1, goto_home, pick_menu_item, pre assert w0 == w1 == w2 == word +def test_file_picker_suffixes(pick_menu_item, goto_home, cap_story, microsd_wipe, press_select, + microsd_path): + # make sure no .txt, .7z & .pdf files are not on the SD card + microsd_wipe() + # create files that must not be recognized, because they're missing the dot + for fn in ["backup7z", "backuptxt", "template:pdf"]: + with open(microsd_path(fn), "w") as f: + f.write("dummy") + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Danger Zone") + pick_menu_item("I Am Developer.") + pick_menu_item("Restore Bkup") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: *.7z,*.txt" in story + press_select() + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("Paper Wallets") + press_select() + pick_menu_item("Don't make PDF") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: *.pdf" in story + + goto_home() + pick_menu_item("Advanced/Tools") + pick_menu_item("File Management") + pick_menu_item("Sign Text File") + time.sleep(.1) + _, story = cap_story() + assert "No suitable files found" in story + assert "The filename must end in: *.txt,*.json" in story + microsd_wipe() + + @pytest.mark.onetime def test_dump_menutree(sim_execfile): # saves to ../unix/work/menudump.txt