What changed, and why it matters
This commit only changes test files in the COLDCARD firmware repository. It adds setup steps (like returning to the home screen before tests), updates menu navigation to match renamed menu items, and makes test assertions more flexible. There are no changes to the actual firmware or wallet code that users rely on, so this cannot directly affect device security.
No security action required. Treat as routine test maintenance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is confined to the testing/ directory. Changes include: adding goto_home() calls at the start of numerous CCC (cosigner-controlled spending) tests; replacing a hardcoded menu path Advanced/Tools -> Backup with Advanced/Tools -> File Management -> Verify Backup; changing assert m[-2] == 'Export' to assert 'Export' in m; adding cleanup via garbage_collector and enable_nfc() in a file-share test; and adding missing press_cancel() teardown. These are test-harness stabilizations, not product code modifications.
Changed components
testing/conftest.pytesting/test_ccc.pytesting/test_notes.pytesting/test_sign.pytesting/test_ux.pyInspect captured patch +69 / −55
diff --git a/testing/conftest.py b/testing/conftest.py
index ad876f3..4634325 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -2244,7 +2244,7 @@ def verify_backup_file(goto_home, pick_menu_item, cap_story, need_keypress):
# Check on-device verify UX works.
goto_home()
pick_menu_item('Advanced/Tools')
- pick_menu_item('Backup')
+ pick_menu_item('File Management')
pick_menu_item('Verify Backup')
time.sleep(0.1)
pick_menu_item(os.path.basename(fn))
diff --git a/testing/test_ccc.py b/testing/test_ccc.py
index 3b75be3..e3a88b7 100644
--- a/testing/test_ccc.py
+++ b/testing/test_ccc.py
@@ -631,8 +631,9 @@ def policy_sign(start_sign, end_sign, cap_story, get_last_violation):
@pytest.mark.parametrize("mag", [1000000, None, 2])
def test_ccc_magnitude(mag_ok, mag, setup_ccc, ccc_ms_setup,
bitcoind, settings_set, policy_sign,
- bitcoind_create_watch_only_wallet):
+ bitcoind_create_watch_only_wallet, goto_home):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -669,8 +670,8 @@ def test_ccc_magnitude(mag_ok, mag, setup_ccc, ccc_ms_setup,
@pytest.mark.parametrize("whitelist_ok", [True, False])
def test_ccc_whitelist(whitelist_ok, setup_ccc, ccc_ms_setup,
bitcoind, settings_set, policy_sign,
- bitcoind_create_watch_only_wallet):
-
+ bitcoind_create_watch_only_wallet, goto_home):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -704,9 +705,9 @@ def test_ccc_whitelist(whitelist_ok, setup_ccc, ccc_ms_setup,
def test_ccc_whitelist_nfc_import(setup_ccc, settings_set, pick_menu_item, cap_story,
cap_menu, press_select, press_nfc, nfc_write_text,
- settings_get, skip_if_useless_way, is_q1):
+ settings_get, skip_if_useless_way, is_q1, goto_home):
skip_if_useless_way("nfc")
-
+ goto_home()
settings_set("ccc", None)
addr = "bcrt1qlk39jrclgnawa42tvhu2n7se987qm96qg8v76e"
@@ -753,8 +754,8 @@ def test_ccc_whitelist_nfc_import(setup_ccc, settings_set, pick_menu_item, cap_s
def test_ccc_velocity(velocity_mi, setup_ccc, ccc_ms_setup, bitcoind, settings_set,
policy_sign, settings_get, bitcoind_create_watch_only_wallet,
enter_enabled_ccc, pick_menu_item, cap_story, need_keypress,
- press_select, press_cancel):
-
+ press_select, press_cancel, goto_home):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -857,8 +858,8 @@ def test_ccc_velocity(velocity_mi, setup_ccc, ccc_ms_setup, bitcoind, settings_s
@pytest.mark.bitcoind
def test_ccc_warnings(setup_ccc, ccc_ms_setup, bitcoind, settings_set, policy_sign,
- bitcoind_create_watch_only_wallet, settings_get):
-
+ bitcoind_create_watch_only_wallet, settings_get, goto_home):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -917,9 +918,10 @@ def test_ccc_warnings(setup_ccc, ccc_ms_setup, bitcoind, settings_set, policy_si
def test_maxed_out(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, sim_exec,
bitcoind, settings_get, load_export, press_cancel, restore_main_seed,
bitcoind_create_watch_only_wallet, policy_sign, goto_eph_seed_menu,
- pick_menu_item, word_menu_entry, press_select, import_multisig):
+ pick_menu_item, word_menu_entry, press_select, import_multisig, goto_home):
# - maxed out values: 24 words, 25 whitelisted p2wsh values
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -980,10 +982,11 @@ def test_maxed_out(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, sim
def test_ccc_whitelist_overlimit_no_mutation(settings_set, setup_ccc, enter_enabled_ccc,
ccc_ms_setup, bitcoind_create_watch_only_wallet,
settings_get, pick_menu_item, cap_menu, cap_story,
- cap_screen, scan_a_qr, press_select, press_cancel,
- is_q1, microsd_path, need_keypress, restore_main_seed):
+ cap_screen, scan_a_qr, press_select, press_cancel, goto_home,
+ is_q1, microsd_path, need_keypress):
# An over-limit whitelist import must be rejected WITHOUT having already
# mutated the (settings-backed) policy address list.
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -1039,15 +1042,15 @@ def test_ccc_whitelist_overlimit_no_mutation(settings_set, setup_ccc, enter_enab
press_cancel()
press_cancel()
- restore_main_seed()
@pytest.mark.parametrize("seed_vault", [True, False])
def test_load_and_sign_key_C(settings_set, setup_ccc, enter_enabled_ccc, ccc_ms_setup, sim_exec,
bitcoind_create_watch_only_wallet, pick_menu_item, load_export,
cap_story, press_cancel, bitcoind, policy_sign, restore_main_seed,
- verify_ephemeral_secret_ui, word_menu_entry, import_multisig,
+ verify_ephemeral_secret_ui, word_menu_entry, import_multisig, goto_home,
press_select, settings_get, seed_vault, confirm_tmp_seed):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
@@ -1384,10 +1387,11 @@ def test_ms_setup_cosigner_import(way, ftype, is_bbqr, N, goto_home, settings_se
def test_ccc_challenge_qr_bad_checksum_crash(setup_ccc, goto_ccc_menu, cap_story, need_keypress,
press_select, press_cancel, scan_a_qr, sim_exec,
- settings_set, is_q1):
+ settings_set, is_q1, goto_home):
if not is_q1:
pytest.skip('Q1 only (QR scan path)')
+ goto_home()
settings_set('ccc', None)
settings_set('seedvault', False) # avoid seed-vault bypass path
@@ -1426,8 +1430,9 @@ def test_ccc_challenge_qr_bad_checksum_crash(setup_ccc, goto_ccc_menu, cap_story
def test_ccc_magnitude_cancel_preserves_value(setup_ccc, enter_enabled_ccc, settings_set,
- settings_get, pick_menu_item, cap_menu,
+ settings_get, pick_menu_item, cap_menu, goto_home,
press_select, press_cancel, press_delete):
+ goto_home()
settings_set('ccc', None)
c_words = setup_ccc(mag=1) # 1 BTC magnitude
@@ -1452,12 +1457,12 @@ def test_ccc_magnitude_cancel_preserves_value(setup_ccc, enter_enabled_ccc, sett
mag = settings_get('ccc')['pol']['mag']
assert mag == 1
- settings_set('ccc', None)
-
@pytest.mark.bitcoind
def test_ccc_whitelist_op_return(setup_ccc, ccc_ms_setup, bitcoind, settings_set,
- policy_sign, bitcoind_create_watch_only_wallet):
+ policy_sign, bitcoind_create_watch_only_wallet,
+ goto_home):
+ goto_home()
settings_set("ccc", None)
settings_set("chain", "XRT")
settings_set("multisig", [])
diff --git a/testing/test_notes.py b/testing/test_notes.py
index 54437c7..6eb01e7 100644
--- a/testing/test_notes.py
+++ b/testing/test_notes.py
@@ -388,7 +388,7 @@ def test_huge_notes(size, encoding, goto_notes, enter_text, cap_menu, need_keypr
time.sleep(.5) # decompression time in some cases
m = cap_menu()
- assert m[-2] == 'Export'
+ assert 'Export' in m
notes = settings_get('notes')
assert len(notes) == 1
@@ -700,6 +700,7 @@ def test_sign_note_body(msg, addr_fmt, acct, need_some_notes,
def test_send_password_menu_item(need_some_passwords, goto_notes, cap_menu, pick_menu_item,
settings_set, settings_remove, press_cancel):
# covers regression where "Send Password" menu item was only shown when USB was disabled
+ settings_set("notes", [])
need_some_passwords()
settings_set('du', 1)
diff --git a/testing/test_sign.py b/testing/test_sign.py
index c38b7a1..70e6a6d 100644
--- a/testing/test_sign.py
+++ b/testing/test_sign.py
@@ -3616,15 +3616,19 @@ def test_tx_explorer_goto_idx_single_item_yikes(mi, fake_txn, start_sign, cap_st
menu = cap_menu()
assert "Inputs" in menu
assert "Outputs" in menu
+ press_cancel()
+ press_cancel()
def test_tx_explorer_goto_idx(fake_txn, start_sign, cap_story, use_testnet, need_keypress,
- pick_menu_item, cap_screen, enter_number, press_cancel, is_q1):
+ pick_menu_item, cap_screen, enter_number, press_cancel, is_q1,
+ goto_home):
use_testnet()
num_ins = 27
num_outs = 32
psbt = fake_txn(num_ins, num_outs, segwit_in=True, change_outputs=[0])
+ goto_home()
start_sign(psbt)
title, story = cap_story()
assert title == "OK TO SEND?"
@@ -3686,7 +3690,7 @@ def test_tx_explorer_goto_idx(fake_txn, start_sign, cap_story, use_testnet, need
def test_input_explorer_foreign_bad_sighash(fake_txn, start_sign, cap_story,
need_keypress, pick_menu_item, press_cancel,
- use_testnet):
+ use_testnet, goto_home):
# PSBT has a foreign input (not ours) carrying a PSBT_IN_SIGHASH_TYPE value
# outside ALL_SIGHASH_FLAGS. consider_dangerous_sighash() only validates
# our-key inputs, so the PSBT passes validation and reaches the approval UX.
@@ -3703,6 +3707,7 @@ def test_input_explorer_foreign_bad_sighash(fake_txn, start_sign, cap_story,
psbt.inputs[0].sighash = 0x05
psbt = fake_txn(2, 2, segwit_in=True, psbt_hacker=hack)
+ goto_home()
start_sign(psbt)
time.sleep(.1)
title, _ = cap_story()
@@ -3745,7 +3750,9 @@ def test_txn_nVersion_zero(segwit, fake_txn, start_sign, cap_story, goto_home):
@pytest.mark.parametrize("segwit_in", [True, False])
@pytest.mark.parametrize("num_ins", [2, 110])
-def test_duplicate_inputs(segwit_in, num_ins, fake_txn, start_sign, end_sign, cap_story):
+def test_duplicate_inputs(segwit_in, num_ins, fake_txn, start_sign, end_sign, cap_story,
+ goto_home):
+ goto_home()
psbt = fake_txn(num_ins, 2, segwit_in=segwit_in, dupe_ins=[num_ins-1])
start_sign(psbt)
title, story = cap_story()
@@ -3755,7 +3762,8 @@ def test_duplicate_inputs(segwit_in, num_ins, fake_txn, start_sign, end_sign, ca
assert title == "OK TO SEND?"
-def test_txid_qr(fake_txn, start_sign, cap_story, press_cancel, press_select):
+def test_txid_qr(fake_txn, start_sign, cap_story, press_cancel, press_select, goto_home):
+ goto_home()
psbt = fake_txn(1, 2, change_outputs=[0])
start_sign(psbt, finalize=False)
press_select() # confirm signing
diff --git a/testing/test_ux.py b/testing/test_ux.py
index 8c262f4..f8b9697 100644
--- a/testing/test_ux.py
+++ b/testing/test_ux.py
@@ -1056,48 +1056,48 @@ def test_qr_share_files(fname, pick_menu_item, goto_home, is_q1, cap_menu, cap_s
@pytest.mark.parametrize("way", ["nfc", "qr"])
def test_share_binary_txn_file(way, goto_home, pick_menu_item, src_root_dir, sim_root_dir,
- press_select, cap_story, cap_screen_qr, is_q1,
- nfc_read, nfc_block4rf):
+ press_select, cap_story, cap_screen_qr, is_q1,enable_nfc,
+ nfc_read, nfc_block4rf, garbage_collector):
if way == "qr" and not is_q1:
pytest.skip("QR share is Q1 only")
+ if way == "nfc":
+ enable_nfc()
+
with open(f"{src_root_dir}/testing/data/devils-txn.txn", "r") as f:
binary = bytes.fromhex(f.read().strip())
assert binary[2:8] != bytes(6)
fname = "binary-l01.txn"
dst = f"{sim_root_dir}/MicroSD/{fname}"
+ garbage_collector.append(dst)
with open(dst, "wb") as f:
f.write(binary)
- try:
- goto_home()
- pick_menu_item("Advanced/Tools")
- pick_menu_item("File Management")
- pick_menu_item("NFC File Share" if way == "nfc" else "QR File Share")
- time.sleep(.1)
- pick_menu_item(fname)
- time.sleep(.2)
+ goto_home()
+ pick_menu_item("Advanced/Tools")
+ pick_menu_item("File Management")
+ pick_menu_item("NFC File Share" if way == "nfc" else "QR File Share")
+ time.sleep(.1)
+ pick_menu_item(fname)
+ time.sleep(.2)
- title, story = cap_story()
- assert "ERROR" not in title
-
- if way == "nfc":
- nfc_block4rf()
- res = nfc_read()
- got_txn = None
- for got in ndef.message_decoder(res):
- if got.type == 'urn:nfc:ext:bitcoin.org:txn':
- got_txn = bytes(got.data)
- break
- assert got_txn == binary
- press_select()
- else:
- qr = cap_screen_qr()
- assert qr.decode().lower() == b2a_hex(binary).decode().lower()
- finally:
- try: os.remove(dst)
- except OSError: pass
+ title, story = cap_story()
+ assert "ERROR" not in title
+
+ if way == "nfc":
+ nfc_block4rf()
+ res = nfc_read()
+ got_txn = None
+ for got in ndef.message_decoder(res):
+ if got.type == 'urn:nfc:ext:bitcoin.org:txn':
+ got_txn = bytes(got.data)
+ break
+ assert got_txn == binary
+ press_select()
+ else:
+ qr = cap_screen_qr()
+ assert qr.decode().lower() == b2a_hex(binary).decode().lower()
@pytest.mark.parametrize("word,cs_word", [
@@ -1120,11 +1120,11 @@ def test_q1_24_8char_words(set_seed_words, is_q1, goto_home, pick_menu_item, pre
if not is_q1:
raise pytest.skip("only Q")
- goto_home()
# longest words in wordlist_en have 8 chars
words = ([word] * 23) + [cs_word]
set_seed_words(" ".join(words))
+ goto_home()
pick_menu_item("Advanced/Tools")
pick_menu_item("Danger Zone")
pick_menu_item("Seed Functions")
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.