v3.10.0 review fixes: repair /api/ram-install-app, escape quotes in esc

- /api/ram-install-app referenced undefined spi1/tar (the resolution block
  was only added to /api/ram-install and /api/cap-compat), so every request
  answered 500.  The same preset/MSL resolution is now in place, with
  regression tests for both modes (install / make selectable) and for the
  no-MSL refusal before any card I/O.
- esc() now escapes " and ' too: the TAR editor renders free-text
  descriptions (and other values) into attribute contexts, so a crafted
  preset import could inject attributes.  Three tests asserting the old
  unescaped output updated; a dedicated esc test added.
- _spi1_for_tar names the no-TAR case in its hint (a pre-built packet
  without tar/spi1 used to report "TAR ?").
- preset free TAR rows no longer store an empty "role" key (the documented
  shape); a non-list `tars` in an update is refused (400) instead of being
  silently ignored.

Live: the no-MSL request answers 400 with the hint; a make_selectable with
a bogus AID runs the format probe and the INSTALL (derived SPI1 16 from the
preset MSL), the card answers remote SW 6A88 and the counter advances - no
500.  Suites: 658 Python + 775 frontend.

No version bump: completes the unreleased v3.10.0 (6360dc5 was never
pushed).  sw cache simple-v359.
This commit is contained in:
2026-10-02 08:40:30 +03:00
parent 6360dc5bae
commit 355bcadb59
10 changed files with 99 additions and 15 deletions
+6 -4
View File
@@ -110,10 +110,11 @@ class StoreCrudTests(unittest.TestCase):
{'tar': 'AF4D02', 'msl': '0A', 'desc': 'My applet'},
{'tar': '', 'msl': '', 'desc': ''}, # a blank editor row
]))
self.assertEqual([(t['role'], t['tar'], t['msl']) for t in p['tars']],
self.assertEqual([(t.get('role', ''), t['tar'], t['msl']) for t in p['tars']],
[('isd', 'AF4D01', '1A'), ('uiccRfm', 'B00000', '16'),
('usimRfm', 'B00001', '16'), ('', 'AF4D02', '0A')])
self.assertEqual(p['tars'][3]['desc'], 'My applet')
self.assertNotIn('role', p['tars'][3]) # free rows carry no role
self.assertEqual(len(p['tars']), 4) # the blank row was dropped
def test_every_tar_needs_its_msl_and_a_unique_tar(self):
@@ -161,9 +162,10 @@ class StoreCrudTests(unittest.TestCase):
{'role': 'usimRfm', 'tar': 'B00001', 'msl': '16'}]})
self.assertEqual(presets.role_tar(upd, 'isd'), 'AF4D01')
self.assertEqual(presets.tar_msl(upd, 'AF4D01'), '1A')
# a malformed tars value must not silently reset the table
again = self.store.update(p['id'], {'tars': 'nonsense'})
self.assertEqual(presets.role_tar(again, 'isd'), 'AF4D01')
# a malformed tars value is refused, not silently ignored or reset
with self.assertRaises(presets.PresetError):
self.store.update(p['id'], {'tars': 'nonsense'})
self.assertEqual(presets.role_tar(self.store.get(p['id']), 'isd'), 'AF4D01')
def test_a_preset_carries_several_keysets(self):
p = self.store.add(preset(keysets=[keyset(kic='15', kid='15'),