diff --git a/frontend/index.html b/frontend/index.html index 77e83d0..427cc65 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2183,6 +2183,12 @@ function hexImageRows(segments, ranges, observed, limit, size) { end = Math.max(end, seg.offset + Math.floor(hex.length / 2)); } const covered = (list, pos) => (list || []).some(([p, n]) => pos >= p && pos < p + n); + // The filler stops at the known file/record length (a short file must not + // look like it has missing data) — and never hides observed bytes: when + // the FCP understates the layout (a 2-byte record_len for a 44-byte + // record read) the data's own end is the limit. Without a known length + // the row also ends at the data instead of inventing bytes. + const stopAt = size && size >= end ? size : end; const rows = []; const used = {changed: false, fresh: false, unknown: false}; let truncated = false; @@ -2197,7 +2203,7 @@ function hexImageRows(segments, ranges, observed, limit, size) { // Never pad past the known file/record length: the filler would // read as missing data when the file is simply short. '--' // still marks bytes inside the file that were never observed. - if (size && pos >= size) break; + if (stopAt && pos >= stopAt) break; const hex = bytes.get(pos); if (hex === undefined) { cells.push({hex: '--', cls: 'hex-unknown'}); @@ -3380,7 +3386,7 @@ function onSearchChange() { } // ──────────────────── Versioning ──────────────────── -const PWA_VERSION = '2.7.4'; +const PWA_VERSION = '2.7.5'; const MIN_SERVER_VERSION = '2.3.0'; function parseVersion(v) { diff --git a/frontend/sw.js b/frontend/sw.js index c8285eb..73bbf29 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simtrace-analyser-v84'; +const CACHE = 'simtrace-analyser-v85'; const PRECACHE = [ 'index.html', diff --git a/frontend/tests/comprion.test.js b/frontend/tests/comprion.test.js index 7541c0c..722918b 100644 --- a/frontend/tests/comprion.test.js +++ b/frontend/tests/comprion.test.js @@ -332,9 +332,18 @@ test('Files view: the hex rows stop at the known length (no filler)', () => { assert.equal(rec.rows[0].cells.length, 9); assert.equal(rec.rows[0].cells.filter(c => c.cls === 'hex-unknown').length, 5); assert.deepEqual(rec.used, {changed: true, fresh: false, unknown: true}); - // Without a known length the old row padding stays (nothing better to do). + // Without a known length the row ends at the data — no invented bytes. const noSize = hexImageRows([{offset: 0, hex: 'aabb'}], [], [], undefined, null); - assert.equal(noSize.rows[0].cells.length, 16); + assert.equal(noSize.rows[0].cells.length, 2); + // A card whose FCP understates the layout (a 2-byte record_len for a + // 44-byte read) must still show every observed byte, not two per row. + const wide = hexImageRows( + [{offset: 0, hex: 'aa'.repeat(44)}], [[0, 2]], [[2, 42]], undefined, 2); + assert.deepEqual(wide.rows.map(r => r.cells.length), [16, 16, 12]); + assert.equal(wide.rows[0].cells[0].cls, 'hex-changed'); + assert.equal(wide.rows[0].cells[2].cls, 'hex-new'); + assert.equal(wide.used.changed, true); + assert.equal(wide.used.fresh, true); }); test('Files view: the hex image respects the row limit', () => { diff --git a/pyproject.toml b/pyproject.toml index fadff98..5d086be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend" [project] name = "simtrace-analyser" -version = "2.7.4" +version = "2.7.5" 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 77c9610..a22159c 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.7.4' +__version__ = '2.7.5' diff --git a/tests/test_pcap_import.py b/tests/test_pcap_import.py index 91f0156..46d77d0 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.7.4') + self.assertEqual(__version__, '2.7.5') def test_status_includes_version(self): import os