From be75be9ed275dcd6e87e2471c0f2670640ae56af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sat, 17 Oct 2026 23:37:30 +0300 Subject: [PATCH] Files view: a card-side change reads as its read, with a "Changed on card!" link (v2.6.4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - a card-side change is discovered by a read operation, so the timeline row now reads "Read EF_IMSI" — the row keeps its warn colour and layout — with "Changed on card!" as the trailing underlined link that opens the change modal; an update row keeps the generic "Details" link - the 'Changed on card' verb string is gone; the new 'Changed on card!' key carries the Russian «Изменено на карте!» - tests: the verb/label cases updated (card_change -> Read, "Read EF_LOCI") and the Files-view wiring test pins the kind-dependent link label; 527 Python + 61 Node tests - README/README_RUS describe the new row wording; PWA_VERSION/pyproject/ version.py 2.6.4, SW cache simtrace-analyser-v78 --- README.md | 5 +++-- README_RUS.md | 9 +++++---- frontend/index.html | 9 +++++---- frontend/sw.js | 2 +- frontend/tests/comprion.test.js | 12 ++++++++---- pyproject.toml | 2 +- simtrace_analyser/version.py | 2 +- tests/test_pcap_import.py | 2 +- 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9a6a518..c1b5599 100644 --- a/README.md +++ b/README.md @@ -203,8 +203,9 @@ The Trace tab has a view selector (*Map* / *Stats* / **Files**) in its topbar, right-aligned over the side panel with the panel's width, so it stays visible while the panel scrolls (a drop-down, since three tab captions no longer fit). The Files view is a -trace-wide timeline of file operations — every **Read**, **Update** and -**Changed on card** row shows the same file name as the Map view, and clicking +trace-wide timeline of file operations — every **Read** and **Update** row +shows the same file name as the Map view (a card-side change appears as a +read with a **Changed on card!** link that opens its details), and clicking a row jumps straight to the APDU in the trace and leaves it highlighted until another one is focused (the APDU list keeps the reader's position: new messages pin the view to the bottom only while the reader is already diff --git a/README_RUS.md b/README_RUS.md index 6dceb56..b380ccb 100644 --- a/README_RUS.md +++ b/README_RUS.md @@ -207,10 +207,11 @@ Application Layer* — два отдельных поля выбора, имен **Files**) — он выровнен по правому краю над боковой панелью и имеет её ширину, поэтому виден всё время, пока панель прокручивается (выпадающий список, три подписи-вкладки уже не помещались). Вид -Files — это таймлайн файловых операций по всей трассе: строки **Read**, -**Update** и **Changed on card** показывают те же имена файлов, что и вид Map, -а щелчок по строке переходит прямо к APDU в трассе и оставляет его -выделенным, пока не выбран другой (список APDU сохраняет позицию читателя: +Files — это таймлайн файловых операций по всей трассе: строки **Read** и +**Update** показывают те же имена файлов, что и вид Map (изменение со стороны +карты выглядит как чтение со ссылкой **Изменено на карте!**, открывающей +подробности), а щелчок по строке переходит прямо к APDU в трассе и оставляет +его выделенным, пока не выбран другой (список APDU сохраняет позицию читателя: новые сообщения прижимают вид к низу, только пока читатель уже внизу, поэтому переход не срывается живым опросом и не снимает выделение). У строк с записанным изменением (изменение со стороны карты либо запись, изменившая или впервые показавшая diff --git a/frontend/index.html b/frontend/index.html index 69518dd..310aee3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2136,7 +2136,8 @@ function fileOpClass(kind) { function fileOpVerb(kind) { if (kind === 'update') return t('Update'); - if (kind === 'card_change') return t('Changed on card'); + // A card-side change is discovered by a read operation, so the row reads + // as one ("Read EF_IMSI") with the change offered as a trailing link. return t('Read'); } @@ -2233,7 +2234,7 @@ async function renderFileView() { } view.innerHTML = timeline.map(op => { const diff = op.change_ref - ? `` + ? `` : ''; return `
${escapeHtml(fileOpLabel(op))}${diff} @@ -3225,7 +3226,7 @@ function onFilterChange() { } // ──────────────────── Versioning ──────────────────── -const PWA_VERSION = '2.6.3'; +const PWA_VERSION = '2.6.4'; const MIN_SERVER_VERSION = '2.3.0'; function parseVersion(v) { @@ -3271,7 +3272,7 @@ const LANG_RU = { 'Files': 'Файлы', 'Read': 'Чтение', 'Update': 'Запись', - 'Changed on card': 'Изменено картой', + 'Changed on card!': 'Изменено на карте!', 'Change details': 'Подробности изменения', 'Details': 'Подробнее', 'changed': 'изменено', diff --git a/frontend/sw.js b/frontend/sw.js index 412a149..04dcd8b 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simtrace-analyser-v77'; +const CACHE = 'simtrace-analyser-v78'; const PRECACHE = [ 'index.html', diff --git a/frontend/tests/comprion.test.js b/frontend/tests/comprion.test.js index ba1ee68..b48c9cf 100644 --- a/frontend/tests/comprion.test.js +++ b/frontend/tests/comprion.test.js @@ -292,10 +292,12 @@ test('Files view: operation kinds map to classes and verbs', () => { assert.equal(fileOpClass('card_change'), 'file-op-card'); assert.equal(fileOpVerb('read'), 'Read'); assert.equal(fileOpVerb('update'), 'Update'); - assert.equal(fileOpVerb('card_change'), 'Changed on card'); + // A card-side change is discovered by a read, so the row reads as a read + // operation ("Read EF_LOCI") with the change as a trailing link. + assert.equal(fileOpVerb('card_change'), 'Read'); assert.equal(fileOpLabel({kind: 'read', file: {name: 'EF_ICCID'}}), 'Read EF_ICCID'); assert.equal(fileOpLabel({kind: 'card_change', file: {name: 'EF_LOCI'}}), - 'Changed on card EF_LOCI'); + 'Read EF_LOCI'); assert.equal(fileOpLabel({kind: 'read', file: null}), 'Read ?'); }); @@ -340,7 +342,9 @@ test('Files view: the view selector (topbar) and the timeline are wired', () => assert.match(src, /\$on\('side-view-select', 'change'/); assert.match(src, /\$on\('file-view', 'click'/); assert.match(src, /API\.getSessionFiles\(sid\)/); - assert.match(src, />\$\{t\('Details'\)\}<\/button>/); + // The trailing link: "Changed on card!" for a card-side change (it opens + // the change modal), the generic "Details" for an update row. + assert.match(src, />\$\{op\.kind === 'card_change' \? t\('Changed on card!'\) : t\('Details'\)\}<\/button>/); assert.match(src, /API\.getSessionFileImage\(STATE\.sessionViewId, fileKey, idx\)/); assert.match(src, /getSessionFileImage\(id, key, change\)/); assert.match(src, /files\/image\?key=/); @@ -354,7 +358,7 @@ test('Files view: strings carry Russian translations', () => { for (const m of lang.matchAll(/'((?:[^'\\]|\\.)*)'\s*:/g)) { keys.add(m[1].replace(/\\(['\\])/g, '$1')); } - for (const s of ['Files', 'Read', 'Update', 'Changed on card', 'Change details', + for (const s of ['Files', 'Read', 'Update', 'Changed on card!', 'Change details', 'Details', 'Close', 'Offset', 'Record', 'Before', 'After', 'Show the change', 'changed', 'newly observed', 'never observed', '{c} bytes changed, {n} bytes newly observed', diff --git a/pyproject.toml b/pyproject.toml index d0f8660..48a918e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend" [project] name = "simtrace-analyser" -version = "2.6.3" +version = "2.6.4" description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)" readme = {file = "README.md", content-type = "text/markdown"} license = {text = "GPL-2.0-or-later"} diff --git a/simtrace_analyser/version.py b/simtrace_analyser/version.py index d522600..825542d 100644 --- a/simtrace_analyser/version.py +++ b/simtrace_analyser/version.py @@ -1,3 +1,3 @@ """Single source of truth for the SIMtrace Analyser version.""" -__version__ = '2.6.3' +__version__ = '2.6.4' diff --git a/tests/test_pcap_import.py b/tests/test_pcap_import.py index af3417f..66909c7 100644 --- a/tests/test_pcap_import.py +++ b/tests/test_pcap_import.py @@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase): class TestVersion(unittest.TestCase): def test_package_version(self): from simtrace2_pysniff import __version__ - self.assertEqual(__version__, '2.6.3') + self.assertEqual(__version__, '2.6.4') def test_status_includes_version(self): import os