Skip to content

Commit bc1f564

Browse files
committed
feat: Solana blind-sign policy tests + report catalog S12-S17
- S12: SPL Token approve - S13: Stake withdraw - S14: Stake deactivate - S15: Show address with QR - S16: Blind-sign REJECTED without AdvancedMode (policy gate) - S17: Blind-sign ALLOWED with AdvancedMode (OLED: "BLIND SIGN" warning) OLED screenshots captured: ENABLE POLICY, BLIND SIGN warning, confirmation. 145 tests, 144 passed, 1 pending (BIP-85 invalid_word_count skip).
1 parent 7cfb81c commit bc1f564

2 files changed

Lines changed: 70 additions & 0 deletions

File tree

scripts/generate-test-report.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,35 @@ def parse_junit(path):
834834
'Compute budget unit price',
835835
'Set priority fee for transaction. OLED shows compute unit price.',
836836
['Unit price']),
837+
('S12', 'test_msg_solana_signtx', 'test_solana_sign_token_approve',
838+
'SPL Token approve',
839+
'Approve delegate to spend tokens. OLED shows approval amount and delegate address.',
840+
['Token approval']),
841+
('S13', 'test_msg_solana_signtx', 'test_solana_sign_stake_withdraw',
842+
'Stake withdraw',
843+
'Withdraw SOL from stake account. OLED shows withdrawal amount.',
844+
['Withdraw amount']),
845+
('S14', 'test_msg_solana_signtx', 'test_solana_sign_stake_deactivate',
846+
'Stake deactivate',
847+
'Begin cooldown period for staked SOL. OLED shows "Deactivate stake?"',
848+
['Deactivate']),
849+
('S15', 'test_msg_solana_getaddress', 'test_solana_show_address',
850+
'Show Solana address on OLED',
851+
'Full 44-char base58 address with QR code and derivation path displayed on OLED. '
852+
'User compares against wallet app to detect address substitution attacks.',
853+
['Solana QR + address']),
854+
('S16', 'test_msg_solana_signtx', 'test_solana_blind_sign_rejected_without_advanced_mode',
855+
'Blind-sign REJECTED without AdvancedMode',
856+
'Unknown program instruction without AdvancedMode policy returns Failure immediately. '
857+
'The device refuses to sign unverified transactions by default — users must explicitly '
858+
'enable AdvancedMode to opt in to blind-signing.',
859+
['Policy rejection']),
860+
('S17', 'test_msg_solana_signtx', 'test_solana_blind_sign_allowed_with_advanced_mode',
861+
'Blind-sign ALLOWED with AdvancedMode',
862+
'With AdvancedMode enabled, OLED shows "BLIND SIGN — Sign unverified Solana transaction? '
863+
'The device cannot fully verify the contents." User must explicitly confirm. This is the '
864+
'power-user escape hatch for DeFi protocols not yet supported by clear-signing.',
865+
['ENABLE POLICY', 'BLIND SIGN warning', 'Confirmation']),
837866
]),
838867

839868
('T', 'TRON', '7.14.0',

tests/test_msg_solana_signtx.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,47 @@ 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 policy → Failure.
335+
OLED: no screen shown — firmware rejects before any user interaction.
336+
This is the default safe behavior: unknown programs cannot be signed."""
337+
self.requires_fullFeature()
338+
self.requires_message("SolanaSignTx")
339+
self.setup_mnemonic_allallall()
340+
# Ensure AdvancedMode is OFF
341+
self.client.apply_policy('AdvancedMode', False)
342+
from_pubkey = self._get_from_pubkey()
343+
# Build tx with a completely unknown program ID
344+
unknown_program = b'\xEE' * 32
345+
instr_data = b'\x01\x02\x03\x04'
346+
raw_tx = self._build_tx(from_pubkey, [], unknown_program, instr_data)
347+
with pytest.raises(CallException) as exc:
348+
self.client.call(messages.SolanaSignTx(
349+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
350+
self.assertIn("AdvancedMode", str(exc.value))
351+
352+
def test_solana_blind_sign_allowed_with_advanced_mode(self):
353+
"""Unknown instruction WITH AdvancedMode policy → "Blind Sign" warning on OLED.
354+
User confirms, device signs. This tests the explicit opt-in for power users."""
355+
self.requires_fullFeature()
356+
self.requires_message("SolanaSignTx")
357+
self.setup_mnemonic_allallall()
358+
# Enable AdvancedMode
359+
self.client.apply_policy('AdvancedMode', True)
360+
from_pubkey = self._get_from_pubkey()
361+
unknown_program = b'\xEE' * 32
362+
instr_data = b'\x01\x02\x03\x04'
363+
raw_tx = self._build_tx(from_pubkey, [], unknown_program, instr_data)
364+
resp = self.client.call(messages.SolanaSignTx(
365+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
366+
self.assertEqual(len(resp.signature), 64)
367+
# Disable AdvancedMode after test
368+
self.client.apply_policy('AdvancedMode', False)
369+
329370

330371
if __name__ == '__main__':
331372
unittest.main()

0 commit comments

Comments
 (0)