v3.19.2: the three-status-words disambiguation (envelope SW vs por.sw)

The script author read por.sw as the envelope SW, so a por.sw expectation
on an applet's own TAR failed with (none).

- docs/test-scripts.md: a 'The three status words (envelope SW / PoR
  status / R-APDU SW)' table in the authoring section - where each one
  comes from, the script field and the report row - plus a cross-reference
  from the por check paragraph; help EN/RU 8.9 points at it.
- a failed por.sw check on a response_type: 'raw' decode carries the check
  detail 'the response is application data - an applet's own TAR has no
  R-APDU SW; assert it with data' (the PWA renders check details).
- the runner's log prints APP DATA=<hex> (no R-APDU SW) for application
  responses instead of the misleading R-APDU SW=(none) data=...; the
  compact/scripting lines are unchanged.
- the step editor's custom-PoR block gets a one-line hint about the same.
Tests: 821 frontend + 730 Python; sw cache simple-v391.
This commit is contained in:
2026-10-04 22:19:58 +03:00
parent 14c43f37b3
commit 78bc91d297
9 changed files with 88 additions and 22 deletions
+31 -6
View File
@@ -1106,6 +1106,31 @@ class TestRunnerActions(RunnerTestCase):
# the live alfa-dsa applet's response (2026-10-04): the secured data
# `80 01 10` is the applet's own, so the compact parse must not claim
# 128 commands - the runner exposes it as the PoR data
preset = {'kic': '15', 'kid': '15', 'kicKey': 'AA' * 16,
'kidKey': 'BB' * 16, 'counter': '00000001',
'tars': preset_tars()}
buf = io.StringIO()
with mock.patch.object(S, '_build_secured_packet', return_value=('AA' * 10, {})), \
mock.patch.object(S, '_send_secured_packet',
return_value={'success': True, 'sw': '9000',
'response_data': self.APP_POR_RAW}):
with contextlib.redirect_stderr(buf):
run = self.run_script(FakeServer(FakeScc()), [
{'type': 'action', 'kind': 'scp80', 'params': {'apdu': '01'},
'check': {'por': {'status': 'por_ok', 'data': '800110'}}},
], preset)
self.assertEqual(run['status'], 'ok', run['steps'])
labels = {c['label']: c for c in run['steps'][0]['checks']}
self.assertTrue(labels['PoR status']['ok'], labels)
self.assertTrue(labels['PoR data']['ok'], labels)
self.assertEqual(run['steps'][0]['por']['response_type'], 'raw')
# the log names application data instead of inventing an R-APDU line
self.assertIn('APP DATA=800110 (no R-APDU SW)', buf.getvalue())
self.assertNotIn('R-APDU SW=', buf.getvalue())
def test_scp80_por_sw_check_on_applet_data_reports_the_hint(self):
# a `por.sw` expectation cannot be satisfied by an applet's own TAR -
# the report explains why on the check row (the live confusion)
preset = {'kic': '15', 'kid': '15', 'kicKey': 'AA' * 16,
'kidKey': 'BB' * 16, 'counter': '00000001',
'tars': preset_tars()}
@@ -1115,13 +1140,13 @@ class TestRunnerActions(RunnerTestCase):
'response_data': self.APP_POR_RAW}):
run = self.run_script(FakeServer(FakeScc()), [
{'type': 'action', 'kind': 'scp80', 'params': {'apdu': '01'},
'check': {'por': {'status': 'por_ok', 'data': '800110'}}},
'check': {'por': {'sw': '9000'}}},
], preset)
self.assertEqual(run['status'], 'ok', run['steps'])
labels = {c['label']: c for c in run['steps'][0]['checks']}
self.assertTrue(labels['PoR status']['ok'], labels)
self.assertTrue(labels['PoR data']['ok'], labels)
self.assertEqual(run['steps'][0]['por']['response_type'], 'raw')
self.assertNotEqual(run['status'], 'ok')
sw_check = [c for c in run['steps'][0]['checks'] if c['label'] == 'PoR SW'][0]
self.assertFalse(sw_check['ok'])
self.assertIn('application data', sw_check.get('detail') or '')
self.assertIn("assert it with 'data'", sw_check.get('detail') or '')
def test_scp80_format_wraps_the_apdu(self):
# `format` wraps the C-APDU in the expanded Command Scripting template