Skip to content

Commit 45b01c7

Browse files
committed
feat: AdvancedMode policy tests for ETH + Solana blind-signing
ETH: - E16: Contract data BLOCKED without AdvancedMode (OLED: "BLOCKED") - E17: Contract data ALLOWED with AdvancedMode (OLED: "CONFIRM ETHEREUM DATA" hex) Solana: - S16: Unknown instruction REJECTED without AdvancedMode - S17: Unknown instruction ALLOWED with AdvancedMode (OLED: "BLIND SIGN") Proves the AdvancedMode policy gate works for both chains: - Default: blind-signing blocked (safe for regular users) - Opt-in: power users enable AdvancedMode, see warning, confirm explicitly
1 parent e76c2db commit 45b01c7

3 files changed

Lines changed: 101 additions & 0 deletions

File tree

scripts/generate-test-report.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,18 @@ def parse_junit(path):
633633
'0x swap ETH to ERC-20', 'DEX aggregator swap via 0x protocol.', []),
634634
('E15', 'test_msg_ethereum_cfunc', 'test_sign_execTx',
635635
'Contract function call', 'Generic contract call signing.', []),
636+
('E16', 'test_msg_ethereum_signtx', 'test_ethereum_contract_data_blocked_without_advanced_mode',
637+
'Contract data BLOCKED without AdvancedMode',
638+
'Without AdvancedMode policy, arbitrary contract data signing is completely blocked. '
639+
'OLED shows "BLOCKED — Blind signing requires AdvancedMode. Enable in device settings." '
640+
'This is the default safe behavior — prevents accidental interaction with malicious contracts.',
641+
['BLOCKED warning']),
642+
('E17', 'test_msg_ethereum_signtx', 'test_ethereum_contract_data_allowed_with_advanced_mode',
643+
'Contract data ALLOWED with AdvancedMode',
644+
'With AdvancedMode enabled, OLED shows "CONFIRM ETHEREUM DATA" with the raw hex bytes '
645+
'and byte count. User must explicitly confirm they understand the contract call data. '
646+
'This is the power-user path for DeFi protocols.',
647+
['CONFIRM ETHEREUM DATA hex']),
636648
]),
637649

638650
('R', 'Ripple (XRP)', '7.0.0',

tests/test_msg_ethereum_signtx.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,5 +498,59 @@ def test_ethereum_signtx_nodata(self):
498498
)
499499

500500

501+
# ================================================================
502+
# AdvancedMode policy tests — blind-sign gate for contract data
503+
# ================================================================
504+
505+
def test_ethereum_contract_data_blocked_without_advanced_mode(self):
506+
"""Contract data WITHOUT AdvancedMode → "Blocked" on OLED, then Failure.
507+
This is the default safe behavior: unknown contract calls cannot be signed
508+
unless the user explicitly enables AdvancedMode. Prevents accidental
509+
interaction with malicious contracts."""
510+
self.requires_fullFeature()
511+
self.setup_mnemonic_nopin_nopassphrase()
512+
# Ensure AdvancedMode is OFF
513+
self.client.apply_policy("AdvancedMode", 0)
514+
515+
try:
516+
self.client.ethereum_sign_tx(
517+
n=[0, 0],
518+
nonce=0,
519+
gas_price=20,
520+
gas_limit=20,
521+
to=binascii.unhexlify("1d1c328764a41bda0492b66baa30c4a339ff85ef"),
522+
value=0,
523+
data=b"\xde\xad\xbe\xef" * 4, # arbitrary contract data
524+
)
525+
self.fail("Expected CallException for blind-sign without AdvancedMode")
526+
except CallException as e:
527+
self.assertIn("policy", str(e).lower())
528+
529+
def test_ethereum_contract_data_allowed_with_advanced_mode(self):
530+
"""Contract data WITH AdvancedMode → shows raw hex data + "Confirm Ethereum Data".
531+
OLED displays the contract call data in hex for the user to verify.
532+
This is the power-user path for interacting with DeFi contracts."""
533+
self.requires_fullFeature()
534+
self.setup_mnemonic_nopin_nopassphrase()
535+
# Enable AdvancedMode
536+
self.client.apply_policy("AdvancedMode", 1)
537+
538+
sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
539+
n=[0, 0],
540+
nonce=0,
541+
gas_price=20,
542+
gas_limit=20,
543+
to=binascii.unhexlify("1d1c328764a41bda0492b66baa30c4a339ff85ef"),
544+
value=0,
545+
data=b"\xde\xad\xbe\xef" * 4,
546+
)
547+
# Should succeed with a valid signature
548+
self.assertIn(sig_v, (27, 28))
549+
self.assertGreater(len(sig_r), 0)
550+
551+
# Cleanup
552+
self.client.apply_policy("AdvancedMode", 0)
553+
554+
501555
if __name__ == "__main__":
502556
unittest.main()

tests/test_msg_solana_signtx.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,41 @@ def test_solana_sign_memo(self):
326326
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
327327
self.assertEqual(len(resp.signature), 64)
328328

329+
# ================================================================
330+
# Blind-sign policy tests — AdvancedMode gate
331+
# ================================================================
332+
333+
def test_solana_blind_sign_rejected_without_advanced_mode(self):
334+
"""Unknown instruction WITHOUT AdvancedMode → Failure.
335+
Default safe behavior: unknown programs cannot be signed."""
336+
self.requires_fullFeature()
337+
self.requires_message("SolanaSignTx")
338+
self.setup_mnemonic_allallall()
339+
self.client.apply_policy('AdvancedMode', False)
340+
from_pubkey = self._get_from_pubkey()
341+
unknown_program = b'\xEE' * 32
342+
instr_data = b'\x01\x02\x03\x04'
343+
raw_tx = self._build_tx(from_pubkey, [], unknown_program, instr_data)
344+
with pytest.raises(CallException) as exc:
345+
self.client.call(messages.SolanaSignTx(
346+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
347+
self.assertIn("AdvancedMode", str(exc.value))
348+
349+
def test_solana_blind_sign_allowed_with_advanced_mode(self):
350+
"""Unknown instruction WITH AdvancedMode → "Blind Sign" warning, user confirms."""
351+
self.requires_fullFeature()
352+
self.requires_message("SolanaSignTx")
353+
self.setup_mnemonic_allallall()
354+
self.client.apply_policy('AdvancedMode', True)
355+
from_pubkey = self._get_from_pubkey()
356+
unknown_program = b'\xEE' * 32
357+
instr_data = b'\x01\x02\x03\x04'
358+
raw_tx = self._build_tx(from_pubkey, [], unknown_program, instr_data)
359+
resp = self.client.call(messages.SolanaSignTx(
360+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
361+
self.assertEqual(len(resp.signature), 64)
362+
self.client.apply_policy('AdvancedMode', False)
363+
329364

330365
if __name__ == '__main__':
331366
unittest.main()

0 commit comments

Comments
 (0)