ui: give the Requires table its own AID column (v3.5.18)
The imported-library AIDs are now shown in a dedicated column between the library name and the minimum version (Library | AID | Version | Family | Refs), monospace and break-all so the 32-char UICC/3GPP AIDs wrap cleanly on narrow panels. When a library name cannot be resolved (vendor AID) the name cell shows a dash instead of repeating the AID now carried by the column. Tests updated: the Requires slice asserts the AID header and every row's AID, and the unresolved-name case expects the dash plus the AID column. Help EN/RU reworded; the AID key is a no-op in RU. 592 frontend / 473 Python green; version 3.5.18; sw simple-v271.
This commit is contained in:
@@ -319,7 +319,7 @@
|
|||||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Операция</th><th class="text-left py-1 px-2">Описание</th></tr></thead>
|
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Операция</th><th class="text-left py-1 px-2">Описание</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Обзор карты (все данные GP)</td><td class="py-1 px-2">Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками <strong>Удалить</strong> для каждого элемента.</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Обзор карты (все данные GP)</td><td class="py-1 px-2">Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками <strong>Удалить</strong> для каждого элемента.</td></tr>
|
||||||
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import, в виде <code class="font-mono text-sm">имя ≥ версия</code> — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
|
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import: имя библиотеки, AID и минимальная версия <code class="font-mono text-sm">≥ x.y</code> — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -318,7 +318,7 @@
|
|||||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item <strong>Delete</strong> buttons.</td></tr>
|
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item <strong>Delete</strong> buttons.</td></tr>
|
||||||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component, shown as <code class="font-mono text-sm">name ≥ version</code> with standard library names resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
|
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -1616,7 +1616,7 @@
|
|||||||
// ===== Version =====
|
// ===== Version =====
|
||||||
// Single source of truth for the PWA version: shown in the header and used
|
// Single source of truth for the PWA version: shown in the header and used
|
||||||
// by the server version check in pysimConnect().
|
// by the server version check in pysimConnect().
|
||||||
const SIMPLE_VERSION = '3.5.17';
|
const SIMPLE_VERSION = '3.5.18';
|
||||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||||
|
|
||||||
// ===== Tab switching =====
|
// ===== Tab switching =====
|
||||||
@@ -8083,12 +8083,15 @@ function capMemHtml(mem) {
|
|||||||
// ">=" requirements: same major, minor >= the export version (4.5.2);
|
// ">=" requirements: same major, minor >= the export version (4.5.2);
|
||||||
// refs = distinct constant-pool references to the package (6.7) ──
|
// refs = distinct constant-pool references to the package (6.7) ──
|
||||||
if (imports.length) {
|
if (imports.length) {
|
||||||
let reqRows = row(head('', 'text-left') + head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
|
let reqRows = row(head('', 'text-left') + head(esc(t('AID')), 'pl-4 text-left') +
|
||||||
|
head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
|
||||||
head(esc(t('Family')), 'pl-4 text-left') + head(esc(t('Refs')), 'pl-4 text-right whitespace-nowrap'));
|
head(esc(t('Family')), 'pl-4 text-left') + head(esc(t('Refs')), 'pl-4 text-right whitespace-nowrap'));
|
||||||
imports.forEach(im => {
|
imports.forEach(im => {
|
||||||
const name = jcAidName(im.aid);
|
const name = jcAidName(im.aid);
|
||||||
const fam = jcAidFamily(im.aid);
|
const fam = jcAidFamily(im.aid);
|
||||||
reqRows += row(cell(name ? esc(name) : '<span class="font-mono break-all">' + esc(im.aid) + '</span>', 'whitespace-nowrap') +
|
// unresolved names show a dash: the AID column carries the identifier
|
||||||
|
reqRows += row(cell(name ? esc(name) : '\u2014', 'whitespace-nowrap') +
|
||||||
|
cell(esc(im.aid), 'pl-4 font-mono break-all') +
|
||||||
num('\u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0)) +
|
num('\u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0)) +
|
||||||
part(esc(fam)) + num(String(Number(im.refs) || 0)));
|
part(esc(fam)) + num(String(Number(im.refs) || 0)));
|
||||||
});
|
});
|
||||||
@@ -16180,6 +16183,7 @@ const LANG_RU = {
|
|||||||
'Compiled': 'Собрано',
|
'Compiled': 'Собрано',
|
||||||
'Requires': 'Требуются',
|
'Requires': 'Требуются',
|
||||||
'Family': 'Семейство',
|
'Family': 'Семейство',
|
||||||
|
'AID': 'AID',
|
||||||
'Refs': 'Ссылки',
|
'Refs': 'Ссылки',
|
||||||
'NVRAM requirement': 'Требование NVRAM',
|
'NVRAM requirement': 'Требование NVRAM',
|
||||||
'code image (load file)': 'образ кода (load-файл)',
|
'code image (load file)': 'образ кода (load-файл)',
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simple-v270';
|
const CACHE = 'simple-v271';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -106,15 +106,14 @@ test('capMemHtml renders the grouped report: package, requires, memory, componen
|
|||||||
assert.ok(out.includes('>24 B</td>'), out);
|
assert.ok(out.includes('>24 B</td>'), out);
|
||||||
assert.ok(out.includes('peak method frame 8 B'), out);
|
assert.ok(out.includes('peak method frame 8 B'), out);
|
||||||
assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
|
assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
|
||||||
// requires: library name, version, family and refs; the AID is not
|
// requires: library name, AID, minimum version, family and refs
|
||||||
// repeated when the name resolves (it adds nothing), and the section
|
|
||||||
// carries no package AIDs
|
|
||||||
assert.ok(out.includes('>javacard.framework</td>'), out);
|
|
||||||
assert.ok(out.includes('>\u2265 1.0</td>'), out);
|
|
||||||
assert.ok(out.includes('>Oracle JavaCard API</td>'), out);
|
|
||||||
assert.ok(out.includes('>6</td>'), out);
|
|
||||||
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
||||||
assert.ok(!req.includes('A0000000620101'), req);
|
assert.ok(req.includes('>AID</td>'), req);
|
||||||
|
assert.ok(req.includes('>javacard.framework</td>'), req);
|
||||||
|
assert.ok(req.includes('>A0000000620101</td>'), req);
|
||||||
|
assert.ok(req.includes('>\u2265 1.0</td>'), req);
|
||||||
|
assert.ok(req.includes('>Oracle JavaCard API</td>'), req);
|
||||||
|
assert.ok(req.includes('>6</td>'), req);
|
||||||
// package identity + compiled-against hint derived from the framework version
|
// package identity + compiled-against hint derived from the framework version
|
||||||
assert.ok(out.includes('A0000000620101') && out.includes('>v1.0</td>'), out);
|
assert.ok(out.includes('A0000000620101') && out.includes('>v1.0</td>'), out);
|
||||||
assert.ok(out.includes('applet package'), out);
|
assert.ok(out.includes('applet package'), out);
|
||||||
@@ -143,12 +142,13 @@ test('capMemHtml skips the import section on a response without imports', () =>
|
|||||||
assert.ok(out.includes('>2.1 kB</td>'), out);
|
assert.ok(out.includes('>2.1 kB</td>'), out);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('capMemHtml keeps unknown import AIDs bare and labels the family only when known', () => {
|
test('capMemHtml shows a dash for an unresolved import name (the AID column carries it)', () => {
|
||||||
const mem = memFixture();
|
const mem = memFixture();
|
||||||
mem.imports = [{ aid: 'A1130001180001', minor: 0, major: 1, refs: 2 }];
|
mem.imports = [{ aid: 'A1130001180001', minor: 0, major: 1, refs: 2 }];
|
||||||
const out = capMemHtml(mem);
|
const out = capMemHtml(mem);
|
||||||
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
|
||||||
assert.ok(req.includes('A1130001180001'), req); // unknown AID: shown as the name
|
assert.ok(req.includes('>\u2014</td>'), req); // no resolved name: a dash
|
||||||
|
assert.ok(req.includes('>A1130001180001</td>'), req); // the AID column carries it
|
||||||
assert.ok(req.includes('>\u2265 1.0</td>'), req);
|
assert.ok(req.includes('>\u2265 1.0</td>'), req);
|
||||||
assert.ok(req.includes('>2</td>'), req); // refs
|
assert.ok(req.includes('>2</td>'), req); // refs
|
||||||
assert.ok(!out.includes('Java Card'), out); // no framework import
|
assert.ok(!out.includes('Java Card'), out); // no framework import
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pysim-simple-server"
|
name = "pysim-simple-server"
|
||||||
version = "3.5.17"
|
version = "3.5.18"
|
||||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
|||||||
from osmocom.utils import rpad
|
from osmocom.utils import rpad
|
||||||
|
|
||||||
|
|
||||||
VERSION = '3.5.17'
|
VERSION = '3.5.18'
|
||||||
|
|
||||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user