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
+7 -1
View File
@@ -58,6 +58,12 @@ function fakeAlert() {
return seen;
}
test('esc escapes quotes as well as the angle brackets', () => {
// the output is used inside attribute values (value="…", title="…",
// label="…") as well as in text contexts (v3.10.0 review fix)
assert.strictEqual(esc('a"b\'c<d>&'), 'a&quot;b&#39;c&lt;d&gt;&amp;');
});
test('cardsNormIccid accepts digits, separators and raw EF hex', () => {
assert.strictEqual(cardsNormIccid(DIGITS), DIGITS);
assert.strictEqual(cardsNormIccid(' 89 70 1190-0000 4600 098 '), DIGITS);
@@ -194,7 +200,7 @@ test('cardsRender highlights the equipped card preset', () => {
let rows = els['cards-tbody'].innerHTML.split('</tr>');
assert.ok(!/bg-emerald-50/.test(rows[0]), 'the other preset must stay plain');
assert.match(rows[1], /bg-emerald-50/);
assert.match(rows[1], /The equipped card's preset/);
assert.match(rows[1], /The equipped card&#39;s preset/);
// the same card stored as raw EF hex matches too
globalThis.cards[1].iccid = RAW_HEX;
cardsRender();