unreleased regression introduced in 300323f18d20d6e0a904f5f3bcc9d63d84d2f6a7
What changed, and why it matters
This commit fixes a user-interface bug in COLDCARD firmware where pressing certain menu keys (like '1' to change account number or '2' to toggle a format option) would accidentally fall through and trigger an export using default settings, instead of waiting for the user to confirm with 'y'. The fix adds 'continue' statements so the device loops back to the menu, and changes the confirmation check to also accept '1' as a valid choice after account entry. It is described by the developer as an unreleased regression, meaning it was caught before reaching users.
No urgent action required for end users because the regression was unreleased. Ensure the fix is included in the next firmware release and add regression tests for menu key handling in export flows to prevent similar fall-through bugs.
Security signals we found
UI control-flow regression could cause unintended export with default account/format
Missing continue statements allowed key handlers to fall through to export logic
Confirmation guard was too strict, potentially aborting legitimate account selection flow
Developer self-describes change as fixing an 'unreleased regression'
Evidence from the diff
The patch corrects control-flow regressions in shared/actions.py export skeleton functions. In export_xpub, toggling slip132 (ch==‘2’) or entering an account number (ch==‘1’) previously fell through to path.format(acct=acct) and subsequent export logic; now those branches continue the selection loop. In electrum_skeleton, ss_descriptor_skeleton, key_expression_skeleton, dump_summary, _generic_export, and unchained_capital_export, the guard that aborts unless ch==’y’ or acct is None was broadened to accept ch in ‘1y’. This prevents the scenario where a user pressing ‘1’ to set an account is then treated as not confirming and the function returns, while also preventing the prior fall-through bug from silently exporting with default account 0.
Changed components
shared/actions.pyexport_xpubelectrum_skeletonss_descriptor_skeletonkey_expression_skeletondump_summary_generic_exportunchained_capital_exportInspect captured patch +8 / −6
diff --git a/shared/actions.py b/shared/actions.py
index 4453548..943e9c7 100644
--- a/shared/actions.py
+++ b/shared/actions.py
@@ -1100,6 +1100,7 @@ async def export_xpub(label, _2, item):
if ch == 'x': return
if ch == "2":
slip132 = not slip132
+ continue
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:')
@@ -1107,6 +1108,7 @@ async def export_xpub(label, _2, item):
pth_split = path.split("/")
pth_split[-1] = ("%dh" % acct)
path = "/".join(pth_split)
+ continue
# assume zero account if not picked
path = path.format(acct=acct)
@@ -1148,7 +1150,7 @@ async def electrum_skeleton(a, b, item):
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:')
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
rv = [
@@ -1181,7 +1183,7 @@ async def ss_descriptor_skeleton(_0, _1, item):
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:', unlimited=True)
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
if int_ext is None:
@@ -1218,7 +1220,7 @@ async def key_expression_skeleton(_0, _1, item):
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:', unlimited=True)
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
# element on 2nd index is address format for signed exports
@@ -1292,7 +1294,7 @@ without ever connecting this Coldcard to a computer.\
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:')
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
# no choices to be made, just do it.
@@ -1314,7 +1316,7 @@ async def _generic_export(prompt, label, f_pattern):
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:')
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
await export_contents(label, lambda: generate_generic_export(acct),
@@ -1366,7 +1368,7 @@ This saves multisig XPUB information required to setup on the Unchained platform
if ch == '1':
acct = await ux_enter_bip32_index('Account Number:')
- if (ch != 'y') or acct is None:
+ if (ch not in '1y') or acct is None:
return
xfp = xfp2str(settings.get('xfp', 0))
Why this scored 23/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.