EF content decoders: decoded fields in the file manager, snapshots, checks and comparisons (v2.4.0)
Client-side, offline decoder layer (ported from simtrace-analyser, verified against the pinned specs and pySim test vectors): - file manager Decoded pill: decoded field table instead of the raw pySim JSON dump (per-record fields for record files); the server's read_*_decoded JSON stays behind a 'pySim JSON (server)' disclosure; switching Raw/Decoded re-reads the open file - snapshot view: decoded field block for transparent files and a per-record decoded summary for record files - profile check / snapshot comparison report: content mismatches of files with a decoder render as a per-field decoded diff (only the differing fields, expected | actual); the raw expected/actual pair is kept when the decoded values match - coverage: ICCID, IMSI/IST, UST/SST, LI/PL, SPN, PLMNsel/PLMNwAcT/ OPLMNwAcT/HPLMNwAcT, OPL, SPDI, PNN, LOCI/PSLOCI/EPSLOCI/EPSNSC/LOCIGPRS, Kc/KcGPRS, ACC, PHASE, CBMI/CBMID/CBMIR, ECC, AD, ACL, SMSP, SMS, ARR, DIR, IMPI/DOMAIN/IMPU, ADN/FDN/SDN/MSISDN/LND/BDN, EXT1-5, SUME (TS 51.011 §10.3.34); unknown files fall back to raw - new frontend/tests/ef_decode.test.js (14 tests); help EN/RU + READMEs document the decoded views; version 2.4.0, SW cache simple-v191 - also fix the README/RUS snapshot-comparison text: comparison is always exact (no content masking)
This commit is contained in:
@@ -500,7 +500,7 @@ Browse the UICC filesystem in a tree view. Files are shown with names, FIDs, and
|
||||
- Entries are grouped with DFs above EFs and sorted by **FID** or symbolic **Name** (pills above the tree, remembered in `localStorage`)
|
||||
- **Read** — reads the selected file (auto-detects transparent vs record files)
|
||||
- **Edit** — switch to edit mode, modify hex data, click **Save** to write back
|
||||
- **Raw / Decoded** — toggle between hex dump and pysim-decoded JSON view
|
||||
- **Raw / Decoded** — toggle between the hex dump and a decoded field table (client-side decoders for IMSI, ICCID, SPN, PLMN lists, LOCI/EPSLOCI, ADN/MSISDN, service tables, SUME, …); the server-side pySim JSON of the same read stays behind a collapsed disclosure
|
||||
- Selecting a file shows its FID, file type, size / record layout and the decoded FCI above the contents
|
||||
- Missing files are shown in red (✗); a present but empty DF shows `(empty)`
|
||||
- **Probe all files** — walks the whole tree (incl. custom files), marks every entry present/absent with *N / total* progress, stoppable, and ends with a summary; browsing itself stays lazy
|
||||
@@ -525,7 +525,7 @@ A filesystem rule is defined by:
|
||||
- **File attributes** — file type, size, record length and record count, taken from the FCP template (any may be left unset).
|
||||
- **Check contents** (optional) — **Exact** hex equality, or **Mask** where `?` is a per-nibble wildcard (a mask with no `?` is a prefix match, e.g. `0891` for the IMSI MCC/MNC). Record files store a per-record list.
|
||||
|
||||
The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results header reads `Profile verification results for: <profile> → <card ICCID>` (or `… → <snapshot name>`; snapshot comparison: `Snapshot comparison results: <master> → <checked>`). **Only mismatches** in the results header hides all passing files and keeps failures and errors only.
|
||||
The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, shows content mismatches of files with a decoder as a decoded per-field comparison (only the differing fields, expected and actual side by side; the raw pair is kept when the decoded values match), and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results header reads `Profile verification results for: <profile> → <card ICCID>` (or `… → <snapshot name>`; snapshot comparison: `Snapshot comparison results: <master> → <checked>`). **Only mismatches** in the results header hides all passing files and keeps failures and errors only.
|
||||
|
||||
#### “Profile from card” scan options
|
||||
|
||||
@@ -533,12 +533,12 @@ The scan dialog asks for a profile name and offers the FCP/FCI mode described ab
|
||||
|
||||
#### Card snapshots
|
||||
|
||||
The list view has two tabs — **Profiles** and **Card snapshots**. A snapshot is an immutable capture of the card filesystem: for every existing file it stores the path, symbolic name, file type, size (or record length/count), the raw FCI from the SELECT response, and the contents whenever the file is readable (no ignore list, no masking). The ICCID is decoded from EF.ICCID and shown next to the snapshot name. The scan also measures every card command (SELECT / READ BINARY / READ RECORD) from command to response and stores min/avg/max per type plus the total scan time; the snapshot view shows these in the summary and the select/read time per file (read time per record). Timings are display-only and ignored by checks/comparisons.
|
||||
The list view has two tabs — **Profiles** and **Card snapshots**. A snapshot is an immutable capture of the card filesystem: for every existing file it stores the path, symbolic name, file type, size (or record length/count), the raw FCI from the SELECT response, and the contents whenever the file is readable (no ignore list, no masking). The ICCID is decoded from EF.ICCID and shown next to the snapshot name. Captured contents are shown with their decoded form where a decoder exists — a field table for transparent files and a one-line summary per record for record files. The scan also measures every card command (SELECT / READ BINARY / READ RECORD) from command to response and stores min/avg/max per type plus the total scan time; the snapshot view shows these in the summary and the select/read time per file (read time per record). Timings are display-only and ignored by checks/comparisons.
|
||||
|
||||
- **New snapshot** scans the card; **Import snapshot** loads JSON.
|
||||
- Each snapshot row has **Open** (all captured data read-only, raw FCI with decoded FCI and contents; only the name is editable), **Export**, and **Delete**.
|
||||
- **Check card snapshot** on a profile row runs the profile rules against a snapshot picked from the list, without a card reader. Files whose contents were not captured are reported as unverifiable errors.
|
||||
- **Compare snapshots** compares two snapshots offline exactly like a profile check: pick the *master* snapshot and the *snapshot to check*, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same report. Every file must match exactly (exact FCI, contents); files present only in the checked snapshot are reported as extra files. In the comparison report the mismatch fields and FCI comparison columns are labeled with the master/checked snapshot names instead of expected/actual.
|
||||
- **Compare snapshots** compares two snapshots offline exactly like a profile check: pick the *master* snapshot and the *snapshot to check* and get the same report — snapshot comparison is always exact (no content masking). Files present only in the checked snapshot are reported as extra files. In the comparison report the mismatch fields and FCI comparison columns are labeled with the master/checked snapshot names instead of expected/actual.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+4
-4
@@ -474,7 +474,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
||||
- Элементы сгруппированы (DF выше EF) и отсортированы по **FID** или символьному **имени** (пиллы над деревом, выбор сохраняется в `localStorage`)
|
||||
- **Read** — чтение файла (автоопределение transparent/record)
|
||||
- **Edit** — режим редактирования, измените hex-данные и нажмите **Save** для записи
|
||||
- **Raw / Decoded** — переключение между hex-дампом и декодированным JSON
|
||||
- **Raw / Decoded** — переключение между hex-дампом и таблицей декодированных полей (клиентские декодеры: IMSI, ICCID, SPN, списки PLMN, LOCI/EPSLOCI, ADN/MSISDN, таблицы сервисов, SUME, …); серверный pySim JSON того же чтения остаётся в свёрнутом блоке
|
||||
- При выборе файла над содержимым показываются FID, тип файла, размер / структура записей и декодированный FCI
|
||||
- Отсутствующие файлы показаны красным (✗); существующий пустой DF — `(пусто)`
|
||||
- **Проверить все файлы** — обход всего дерева (включая пользовательские) с пометкой «есть/нет», прогрессом *N / всего*, возможностью остановки и сводкой в конце; сам просмотр остаётся ленивым
|
||||
@@ -499,7 +499,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
||||
- **Атрибуты файла** — тип, размер, длина и число записей из шаблона FCP (любое можно не задавать).
|
||||
- **Проверка содержимого** (опционально) — **Exact** (точное равенство hex) или **Mask**, где `?` — пониббловый джокер (маска без `?` — префиксное совпадение, напр. `0891` для MCC/MNC IMSI). Для record-файлов хранится список записей.
|
||||
|
||||
Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `Результаты проверки профиля: <профиль> → <ICCID карты>` (или `… → <имя снимка>`; для сравнения снимков — `Результаты сравнения снимков: <эталон> → <проверяемый>`). Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки.
|
||||
Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке), показывает расхождения содержимого файлов с декодером декодированным сравнением по полям (только отличающиеся поля, ожидаемое и фактическое рядом; сырая пара остаётся, когда декодированные значения совпадают) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `Результаты проверки профиля: <профиль> → <ICCID карты>` (или `… → <имя снимка>`; для сравнения снимков — `Результаты сравнения снимков: <эталон> → <проверяемый>`). Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки.
|
||||
|
||||
#### Опции сканирования «Профиль с карты»
|
||||
|
||||
@@ -507,12 +507,12 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
||||
|
||||
#### Снимки карт
|
||||
|
||||
Представление списка имеет две вкладки — **«Профили»** и **«Снимки карт»**. Снимок — неизменяемая фиксация файловой системы: путь, символьное имя, тип, размер (или длина/число записей), сырой FCI и содержимое (если читается) каждого существующего файла. ICCID декодируется из EF.ICCID и показывается рядом с именем. При сканировании также измеряется время каждой команды карты (SELECT / READ BINARY / READ RECORD) от отправки до ответа; сохраняются min/сред/max по типам и общее время сканирования — они показываются в сводке снимка и по файлам/записям. Время носит информационный характер и не используется при проверках и сравнении.
|
||||
Представление списка имеет две вкладки — **«Профили»** и **«Снимки карт»**. Снимок — неизменяемая фиксация файловой системы: путь, символьное имя, тип, размер (или длина/число записей), сырой FCI и содержимое (если читается) каждого существующего файла. ICCID декодируется из EF.ICCID и показывается рядом с именем. Захваченное содержимое показывается вместе с декодированным представлением, где есть декодер — таблица полей для transparent-файлов и однострочная сводка по каждой записи для record-файлов. При сканировании также измеряется время каждой команды карты (SELECT / READ BINARY / READ RECORD) от отправки до ответа; сохраняются min/сред/max по типам и общее время сканирования — они показываются в сводке снимка и по файлам/записям. Время носит информационный характер и не используется при проверках и сравнении.
|
||||
|
||||
- **Новый снимок** сканирует карту; **Импорт снимка** загружает JSON.
|
||||
- В строке снимка: **Открыть** (все данные только для чтения, сырой FCI с декодированным и содержимое; редактируется только имя), **Экспорт**, **Удалить**.
|
||||
- **Проверить снимок карты** в строке профиля выполняет правила профиля на выбранном снимке без картридера. Файлы без захваченного содержимого помечаются как непроверяемые ошибки.
|
||||
- **Сравнить снимки** сравнивает два снимка offline так же, как проверка профиля: выберите *эталонный* снимок и *снимок для проверки*, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию). Всё должно совпадать точно (FCI, содержимое); файлы только в проверяемом снимке помечаются как лишние. В отчёте поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual.
|
||||
- **Сравнить снимки** сравнивает два снимка offline так же, как проверка профиля: выберите *эталонный* снимок и *снимок для проверки* и получите тот же отчёт — сравнение снимков всегда точное (без масок содержимого). Файлы только в проверяемом снимке помечаются как лишние. В отчёте поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Прочитать</strong> — чтение файла (автоопределение transparent/record)</li>
|
||||
<li><strong>Редактировать</strong> — изменение hex-данных, <strong>Сохранить</strong> для записи (или <strong>Отмена</strong>)</li>
|
||||
<li><strong>Данные как на карте / Декодированные данные</strong> — переключение между hex-дампом и декодированным JSON</li>
|
||||
<li><strong>Данные как на карте / Декодированные данные</strong> — переключение между hex-дампом и таблицей декодированных полей (клиентские декодеры EF: IMSI, ICCID, SPN, списки PLMN, LOCI/PSLOCI/EPSLOCI, ADN/MSISDN, таблицы сервисов, SUME, …); серверный pySim JSON того же чтения остаётся доступен в свёрнутом блоке <em>pySim JSON (сервер)</em></li>
|
||||
<li><strong>Проверить все файлы</strong> — обход всего дерева (включая пользовательские файлы) с пометкой каждого элемента: есть (обычный вид) или нет (красный ✗, без стрелки разворачивания); существующие пустые DF показывают <code class="font-mono text-sm">(пусто)</code>. Отображается прогресс <em>N / всего</em>, обход можно остановить; в конце — сводка «есть/нет». Файлы проверяются только при разворачивании или проверке — просмотр остаётся ленивым.</li>
|
||||
</ul>
|
||||
|
||||
@@ -417,12 +417,12 @@
|
||||
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
|
||||
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта выводится <em>Результаты проверки профиля: <профиль> → <ICCID карты></em>. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
|
||||
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; расхождения содержимого файлов с декодером показываются декодированным сравнением по полям (только отличающиеся поля, ожидаемое и фактическое рядом; сырая пара остаётся, когда декодированные значения совпадают), а для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта выводится <em>Результаты проверки профиля: <профиль> → <ICCID карты></em>. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
|
||||
<h4 class="font-medium mb-1">Опции сканирования «Профиль с карты»</h4>
|
||||
<p class="text-sm mb-2">Диалог сканирования запрашивает имя профиля и предлагает селектор <strong>«Проверка FCP/FCI»</strong> (те же три режима, по умолчанию <strong>Тип файла + размер (FCP)</strong>), применяемый ко всем создаваемым правилам, а также список <strong>«Игнорировать содержимое файлов»</strong> (все отмечены по умолчанию, кроме <code class="font-mono text-sm">EF.ARR</code>; флажок в заголовке отмечает или снимает весь список) часто перезаписываемых файлов, содержимое которых пропускается: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Ещё две отмеченные по умолчанию опции <strong>«Сравнивать первые 4 байта для»</strong> <code class="font-mono text-sm">EF.IMSI</code> и <code class="font-mono text-sm">EF.ICCID</code> захватывают содержимое этих файлов как маску только первых 4 байт (снимите для точного сравнения). Строка прогресса показывает <em>N / всего файлов</em> с текущим путём файла во время сканирования; при сканировании опции скрываются, а кнопки блокируются. Правила создаются только для файлов, которые реально существуют на карте (возвращён FCP-шаблон); отсутствующие файлы пропускаются. Пользовательские файлы из подвкладки <strong>«Пользовательские файлы»</strong> включаются с той же проверкой существования. Тот же диалог и опции использует <strong>«Профиль из снимка»</strong>: вместо карты обходятся захваченные файлы выбранного снимка; для файлов, содержимое которых не было захвачено, правило создаётся без проверки содержимого (при последующей проверке профиля они помечаются как непроверяемые).</p>
|
||||
|
||||
<h4 id="card-snapshots" class="font-medium mb-1">Снимки карт</h4>
|
||||
<p class="text-sm mb-2">Представление списка имеет две вкладки — <strong>«Профили»</strong> и <strong>«Снимки карт»</strong>. Снимок карты — неизменяемая фиксация файловой системы карты: для каждого существующего файла сохраняются путь, символьное имя, тип, размер (или длина/число записей), сырой FCI из ответа SELECT и содержимое, если файл читается (без списка игнорирования и без масок). ICCID декодируется из EF.ICCID и показывается рядом с именем снимка. При сканировании измеряется время каждой команды карты (SELECT, READ BINARY, READ RECORD) от отправки до ответа; снимок хранит min/сред/max по каждому типу команд и общее время сканирования, а в представлении эти значения показываются в сводке под заголовком, время select/read — для каждого файла и время чтения — для каждой записи. Время носит информационный характер и не используется при проверках и сравнении.</p>
|
||||
<p class="text-sm mb-2">Представление списка имеет две вкладки — <strong>«Профили»</strong> и <strong>«Снимки карт»</strong>. Снимок карты — неизменяемая фиксация файловой системы карты: для каждого существующего файла сохраняются путь, символьное имя, тип, размер (или длина/число записей), сырой FCI из ответа SELECT и содержимое, если файл читается (без списка игнорирования и без масок). ICCID декодируется из EF.ICCID и показывается рядом с именем снимка. Захваченное содержимое показывается вместе с декодированным представлением, где есть декодер — таблица полей для transparent-файлов и однострочная сводка по каждой записи для record-файлов. При сканировании измеряется время каждой команды карты (SELECT, READ BINARY, READ RECORD) от отправки до ответа; снимок хранит min/сред/max по каждому типу команд и общее время сканирования, а в представлении эти значения показываются в сводке под заголовком, время select/read — для каждого файла и время чтения — для каждой записи. Время носит информационный характер и не используется при проверках и сравнении.</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Новый снимок</strong> — запрашивает имя и сканирует карту, затем возвращает к списку.</li>
|
||||
<li><strong>Импорт снимка</strong> — загружает снимок из JSON-файла.</li>
|
||||
|
||||
+3
-3
@@ -350,7 +350,7 @@
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Read</strong> — reads the selected file (auto-detects transparent vs record files)</li>
|
||||
<li><strong>Edit</strong> — modify hex data, <strong>Save</strong> to write back (or <strong>Cancel</strong>)</li>
|
||||
<li><strong>Raw / Decoded</strong> — toggle between hex dump and pySim-decoded JSON</li>
|
||||
<li><strong>Raw / Decoded</strong> — toggle between the hex dump and a decoded field table (client-side EF decoders: IMSI, ICCID, SPN, PLMN lists, LOCI/PSLOCI/EPSLOCI, ADN/MSISDN, service tables, SUME, …); the server-side pySim JSON of the same read stays available in the collapsed <em>pySim JSON (server)</em> disclosure</li>
|
||||
<li><strong>Probe all files</strong> — walks the whole tree (including custom files) and marks every entry present (normal) or absent (red ✗, no expand arrow); empty-but-present DFs show <code class="font-mono text-sm">(empty)</code>. Shows progress <em>N / total</em>, can be stopped, and finishes with a present/absent summary. Files are only verified when expanded or probed — browsing stays lazy.</li>
|
||||
</ul>
|
||||
|
||||
@@ -417,12 +417,12 @@
|
||||
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
||||
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results header reads <em>Profile verification results for: <profile> → <card ICCID></em>. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
|
||||
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; content mismatches of files with a decoder are shown as a decoded per-field comparison instead (only the differing fields, expected and actual side by side; the raw pair is kept when the decoded values match), and FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results header reads <em>Profile verification results for: <profile> → <card ICCID></em>. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
|
||||
<h4 class="font-medium mb-1">“Profile from card” scan options</h4>
|
||||
<p class="text-sm mb-2">The scan dialog asks for a profile name and offers a <strong>“FCP/FCI check”</strong> selector (the same three modes above, default <strong>Filetype + size</strong>) applied to every generated rule, plus an <strong>“Ignore contents of files”</strong> checklist (all checked by default except <code class="font-mono text-sm">EF.ARR</code>; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Two further checked-by-default options <strong>“Match first 4 bytes for”</strong> <code class="font-mono text-sm">EF.IMSI</code> and <code class="font-mono text-sm">EF.ICCID</code> capture those files’ contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows <em>N / total files</em> with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the <strong>Custom files</strong> sub-tab are included under the same existence check. The same dialog and options are used by <strong>Profile from snapshot</strong>, which walks the selected snapshot’s captured files instead of the card; rules for files whose contents were not captured during the scan get no content check (they are reported as unverifiable when the profile is later checked).</p>
|
||||
|
||||
<h4 id="card-snapshots" class="font-medium mb-1">Card snapshots</h4>
|
||||
<p class="text-sm mb-2">The list view has two tabs — <strong>Profiles</strong> and <strong>Card snapshots</strong>. A card snapshot is an immutable capture of the card filesystem: for every existing file it stores the path, symbolic name, file type, size (or record length/count), the raw FCI from the SELECT response, and the contents whenever the file is readable (no ignore list, no masking). The ICCID is decoded from EF.ICCID and shown next to the snapshot name. The scan also measures each card command (SELECT, READ BINARY, READ RECORD) from command to response; the snapshot stores min/avg/max per command type and the total scan time, and the view shows these in the summary under the title plus the select/read times per file and the read time per record. Timings are informational only and are not used by checks or comparisons.</p>
|
||||
<p class="text-sm mb-2">The list view has two tabs — <strong>Profiles</strong> and <strong>Card snapshots</strong>. A card snapshot is an immutable capture of the card filesystem: for every existing file it stores the path, symbolic name, file type, size (or record length/count), the raw FCI from the SELECT response, and the contents whenever the file is readable (no ignore list, no masking). The ICCID is decoded from EF.ICCID and shown next to the snapshot name. Captured contents are shown with their decoded form where a decoder exists — a field table for transparent files and a one-line summary per record for record files. The scan also measures each card command (SELECT, READ BINARY, READ RECORD) from command to response; the snapshot stores min/avg/max per command type and the total scan time, and the view shows these in the summary under the title plus the select/read times per file and the read time per record. Timings are informational only and are not used by checks or comparisons.</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>New snapshot</strong> — asks for a name and scans the card, then returns to the list.</li>
|
||||
<li><strong>Import snapshot</strong> — loads a snapshot from a JSON file.</li>
|
||||
|
||||
+912
-11
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v190';
|
||||
const CACHE = 'simple-v191';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
||||
|
||||
function extractFunc(src, name) {
|
||||
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
||||
const m = re.exec(src);
|
||||
if (!m) throw new Error('function ' + name + ' not found');
|
||||
let i = m.index + m[0].length - 1;
|
||||
let depth = 0;
|
||||
for (; i < src.length; i++) {
|
||||
if (src[i] === '{') depth++;
|
||||
else if (src[i] === '}') {
|
||||
depth--;
|
||||
if (depth === 0) break;
|
||||
}
|
||||
}
|
||||
return src.slice(m.index, i + 1);
|
||||
}
|
||||
|
||||
const FNS = [
|
||||
'efBytes', 'efHex', 'efSwapNibbles', 'efAllFf', 'efRstripFf', 'efTlv', 'efUcs2Be',
|
||||
'efGsm7Octets', 'efAnnexA', 'efBcdAddress', 'efBcdAddressLen', 'efPlmn', 'efPlmnAct', 'efPnnText',
|
||||
'efDecIccid', 'efDecImsi', 'efDecLi', 'efDecServiceTable', 'efDecPlmnList', 'efDecPlmnWact',
|
||||
'efDecSpn', 'efDecLoci', 'efDecPsLoci', 'efDecEpsLoci', 'efDecEpsNsc', 'efDecKc', 'efDecAcc',
|
||||
'efDecPhase', 'efDecCbmi', 'efDecCbmir', 'efDecEcc', 'efDecOpl', 'efDecAd', 'efDecAcl',
|
||||
'efDecSmsp', 'efDecSpdi', 'efDecNai', 'efDecDir', 'efDecArr', 'efDecPnn', 'efDecAdn',
|
||||
'efDecExt1', 'efDecSms', 'efDecSume', 'efFindDecoder', 'efFidFromPath', 'efDecodeBytes',
|
||||
'efDecodeFile', 'efDecodeHex', 'efIsRawOnly', 'efFieldLabel', 'efPrimitive', 'efFlatten',
|
||||
'efDiffData', 'efDataSummary', 'efContentDiff', 'gsm7Decode', 'efRenderFieldsHtml',
|
||||
'pysimFsDecodedHtml', 'profilerRenderReport', 'profilerLabelText', 'profilerResultAspects',
|
||||
'profilerAspectSummary', 'profilerMatchedRecordsText', 'profilerRawDataCheck', 'profilerCustomNameForPath',
|
||||
'pysimCustomNormPath',
|
||||
];
|
||||
|
||||
let code = '';
|
||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||
for (const c of ['GSM7_ALPHABET', 'GSM7_EXT_MAP']) {
|
||||
code += html.match(new RegExp('const ' + c + ' = [\\s\\S]*?\\n\\];'))[0].replace('const ', 'var ') + '\n';
|
||||
}
|
||||
for (const c of ['EF_UST_SERVICES', 'EF_SST_SERVICES', 'EF_IST_SERVICES', 'EF_PHASE_NAMES']) {
|
||||
code += html.match(new RegExp('const ' + c + ' = \\{.*\\};'))[0].replace('const ', 'var ') + '\n';
|
||||
}
|
||||
code += html.match(/const EF_FIELD_LABELS = \{[\s\S]*?\n\};/)[0].replace('const ', 'var ') + '\n';
|
||||
code += html.match(/const EF_DECODERS = \[[\s\S]*?\n\];/)[0].replace('const ', 'var ') + '\n';
|
||||
code += 'var pysimCustomFiles = [];\nvar pysimFsSelected = \'EF.IMSI\';\n';
|
||||
code += 'function esc(s){return String(s);}\nfunction escHtml(s){return String(s);}\nfunction t(k){return k;}\n';
|
||||
eval(code);
|
||||
|
||||
test('ef helpers: bytes, hex, swap, TLV, PLMN', () => {
|
||||
assert.strictEqual(efHex(efBytes('a0ff01')), 'A0FF01');
|
||||
assert.strictEqual(efSwapNibbles('1234'), '2143');
|
||||
assert.strictEqual(efPlmn(efBytes('22F860')).mcc, '228');
|
||||
assert.strictEqual(efPlmn(efBytes('22F860')).mnc, '06');
|
||||
assert.ok(efAllFf(efBytes('FFFF')));
|
||||
assert.ok(!efAllFf(efBytes('FF00')));
|
||||
// 05 03 AA BB CC + 1E 01 DD
|
||||
const tlvs = efTlv(efBytes('0503AABBCC1E01DD'));
|
||||
assert.deepStrictEqual(tlvs.map(t => t.tag), [0x05, 0x1E]);
|
||||
assert.strictEqual(efHex(tlvs[0].value), 'AABBCC');
|
||||
assert.strictEqual(efHex(tlvs[1].value), 'DD');
|
||||
// long-form length
|
||||
assert.strictEqual(efTlv(efBytes('058105' + '00'.repeat(5)))[0].value.length, 5);
|
||||
});
|
||||
|
||||
test('efAnnexA decodes GSM7 octets, UCS2 and packed variants', () => {
|
||||
assert.strictEqual(efAnnexA(efBytes('47534D2D52204348')), 'GSM-R CH'); // one octet per char
|
||||
assert.strictEqual(efAnnexA(efBytes('8000470053004D')), 'GSM'); // UCS2 prefix
|
||||
assert.strictEqual(efAnnexA(efBytes('')), '');
|
||||
assert.strictEqual(efAnnexA(efBytes('FFFFFFFF')), '');
|
||||
});
|
||||
|
||||
test('EF decoders match the pinned spec / pySim test vectors', () => {
|
||||
assert.deepStrictEqual(efDecIccid(efBytes('988812010000400310f0')), { iccid: '8988211000000430010' });
|
||||
assert.deepStrictEqual(efDecImsi(efBytes('082982608200002080')), { imsi: '228062800000208' });
|
||||
assert.deepStrictEqual(efDecLi(efBytes('6465')), { languages: ['de'] });
|
||||
assert.deepStrictEqual(efDecLi(efBytes('FFFF')), { languages: [] });
|
||||
assert.deepStrictEqual(efDecSpn(efBytes('0147534d2d52204348ffffffffffffffff')), { show_in_hplmn: true, hide_in_oplmn: false, name: 'GSM-R CH' });
|
||||
assert.deepStrictEqual(efDecAcc(efBytes('0000')), { access_classes: [] });
|
||||
assert.deepStrictEqual(efDecAcc(efBytes('8000')), { access_classes: [0] });
|
||||
assert.deepStrictEqual(efDecEcc(efBytes('19f101')), { number: '911', esc: '0x01' });
|
||||
assert.deepStrictEqual(efDecKc(efBytes('837d783609a3858f05')), { kc: '837D783609A3858F', ksi: 0 });
|
||||
assert.deepStrictEqual(efDecPlmnList(efBytes('22F860')), { plmns: [{ mcc: '228', mnc: '06' }] });
|
||||
assert.deepStrictEqual(efDecCbmi(efBytes('0010FFFF0020')), { message_ids: [16, 32] });
|
||||
assert.deepStrictEqual(efDecCbmir(efBytes('0000FFFEFFFF')), { ranges: [['0000', 'FFFE']] });
|
||||
assert.deepStrictEqual(efDecPhase(efBytes('03')), { phase: 'phase 2 and higher' });
|
||||
assert.deepStrictEqual(efDecNai(efBytes('8021696d732e6d6e633030302e6d63633733382e336770706e6574776f726b2e6f7267')),
|
||||
{ text: 'ims.mnc000.mcc738.3gppnetwork.org' });
|
||||
assert.deepStrictEqual(efDecImsi(efBytes('FFFF')), { empty: true });
|
||||
assert.deepStrictEqual(efDecIccid(efBytes('FFFFFFFFFFFF')), { empty: true });
|
||||
});
|
||||
|
||||
test('EF.SUME decodes the SET UP MENU title and icon TLVs (TS 51.011 10.3.34)', () => {
|
||||
// 05 05 "Menu!" + 1E 02 01 23 + FF padding
|
||||
const d = efDecSume(efBytes('05054D656E75211E020123FFFFFFFF'));
|
||||
assert.deepStrictEqual(d, { title: 'Menu!', icon: '0123' });
|
||||
assert.deepStrictEqual(efDecSume(efBytes('FFFFFFFF')), { empty: true });
|
||||
// UCS2 title: 05 09 80 00 4D 00 65 00 6E 00 75
|
||||
assert.deepStrictEqual(efDecSume(efBytes('050980004D0065006E0075FFFFFFFF')),
|
||||
{ title: 'Menu' });
|
||||
});
|
||||
|
||||
test('ADN-format records: alpha + BCD number + CCP/EXT1', () => {
|
||||
const d = efDecAdn(efBytes('42204841203120536963FFFFFFFFFFFF06810628560810FFFFFFFFFFFFFF'));
|
||||
assert.strictEqual(d.name, 'B HA 1 Sic');
|
||||
assert.strictEqual(d.number, '6082658001');
|
||||
assert.strictEqual(d.ccp, undefined);
|
||||
assert.strictEqual(d.ext1, undefined);
|
||||
// international number in EF.MSISDN
|
||||
const m = efDecAdn(efBytes('ffffffffffffffffffffffffffffffffffffffff04b12143f5ffffffffffffffffff'));
|
||||
assert.strictEqual(m.name, '');
|
||||
assert.strictEqual(m.number, '12345');
|
||||
// EXT1 per TS 51.011 10.5.10: type 02 (additional data) + count 03 + BCD
|
||||
assert.deepStrictEqual(efDecExt1(efBytes('02032143F5' + 'FF'.repeat(8))), { number: '12345' });
|
||||
});
|
||||
|
||||
test('EF record decoders: SMS, OPL, DIR, ARR, IMPI/SMSP', () => {
|
||||
const opl = efDecOpl(efBytes('62f2100000fffe01'));
|
||||
assert.deepStrictEqual(opl, { mcc: '262', mnc: '01', lac_range: ['0000', 'FFFE'], pnn_record: 1 });
|
||||
const dir = efDecDir(efBytes('61294f10a0000000871002ffffffff890709000050055553696d31730ea00c80011781025f608203454150'));
|
||||
assert.strictEqual(dir.applications.length, 1);
|
||||
assert.strictEqual(dir.applications[0].aid, 'A0000000871002FFFFFFFF8907090000');
|
||||
assert.strictEqual(dir.applications[0].label, 'USim1');
|
||||
const arr = efDecArr(efBytes('800101a40683010a950108'));
|
||||
assert.deepStrictEqual(arr.rules, [{ tag: '0x80', hex: '01' }, { tag: '0xA4', hex: '83010A950108' }]);
|
||||
const impi = efDecNai(efBytes('803137333830303630303030303031303140696d732e6d6e633030302e6d63633733382e336770706e6574776f726b2e6f7267'));
|
||||
assert.strictEqual(impi.text, '738006000000101@ims.mnc000.mcc738.3gppnetwork.org');
|
||||
const smsp = efDecSmsp(efBytes('534d5343ffffffffffffffffffffffffe1ffffffffffffffffffffffff0891945197109099f9ffffff0000a9'));
|
||||
assert.strictEqual(smsp.alpha, 'SMSC');
|
||||
assert.strictEqual(smsp.sca, '+4915790109999');
|
||||
assert.strictEqual(smsp.tp_pid, '0x00');
|
||||
assert.strictEqual(smsp.tp_dcs, '0x00');
|
||||
// EF.SMS status byte + skip SMSC + TPDU
|
||||
const sms = efDecSms(efBytes('030891945197109099f90000'));
|
||||
assert.strictEqual(sms.direction, 'MT');
|
||||
assert.strictEqual(sms.status, 'message to be read');
|
||||
assert.strictEqual(sms.tpdu, '0000');
|
||||
});
|
||||
|
||||
test('service tables: UST service bits and IST fallback for 6F07', () => {
|
||||
const ust = efDecServiceTable(efBytes('00000010'), EF_UST_SERVICES);
|
||||
assert.deepStrictEqual(ust.services, [{ n: 29, name: 'Data download via SMS-CB' }]);
|
||||
const ist = efDecImsi(efBytes('01'));
|
||||
assert.deepStrictEqual(ist.services, [{ n: 1, name: 'P-CSCF address' }]);
|
||||
});
|
||||
|
||||
test('decoder registry resolves by name first, then FID; unknown returns null', () => {
|
||||
assert.strictEqual(efFindDecoder('EF.IMSI', null).fid, '6f07');
|
||||
assert.strictEqual(efFindDecoder(null, '2fe2').name, 'EF.ICCID');
|
||||
assert.strictEqual(efFindDecoder('ADF.USIM/EF.LOCI', null).name, 'EF.LOCI');
|
||||
assert.strictEqual(efFindDecoder('EF.SUME', null).fid, '6f54');
|
||||
assert.strictEqual(efFindDecoder('EF.NOPE', 'ABCD'), null);
|
||||
assert.strictEqual(efFidFromPath('ADF.USIM/6F07'), '6F07');
|
||||
assert.strictEqual(efFidFromPath('MF/7F10/6F3A'), '6F3A');
|
||||
assert.strictEqual(efFidFromPath('ADF.USIM'), null);
|
||||
});
|
||||
|
||||
test('efDecodeFile handles transparent and record content', () => {
|
||||
const t = efDecodeFile('EF.IMSI', null, { kind: 'transparent', data: '082982608200002080' });
|
||||
assert.strictEqual(typeof t.fn, 'function');
|
||||
assert.strictEqual(t.data.imsi, '228062800000208');
|
||||
const r = efDecodeFile('EF.ADN', '6f3a', { kind: 'record', records: [
|
||||
{ num: 1, data: '42204841203120536963FFFFFFFFFFFF06810628560810FFFFFFFFFFFFFF' },
|
||||
{ num: 2, data: 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' },
|
||||
] });
|
||||
assert.strictEqual(r.records.length, 2);
|
||||
assert.strictEqual(r.records[0].data.name, 'B HA 1 Sic');
|
||||
assert.strictEqual(r.records[0].empty, false);
|
||||
assert.strictEqual(r.records[1].empty, true);
|
||||
assert.strictEqual(efDecodeFile('EF.NOPE', null, { kind: 'transparent', data: '00' }), null);
|
||||
});
|
||||
|
||||
test('efFlatten renders fields, services and PLMNs for display', () => {
|
||||
assert.deepStrictEqual(efFlatten({ imsi: '123' }), [['IMSI', '123']]);
|
||||
assert.deepStrictEqual(efFlatten({ services: [{ n: 5, name: 'Call Control by USIM' }] }),
|
||||
[['Service 5', 'Call Control by USIM']]);
|
||||
assert.deepStrictEqual(efFlatten({ plmns: [{ mcc: '262', mnc: '01', access_tech: 'GSM' }] }),
|
||||
[['PLMN 1', '262-01 (GSM)']]);
|
||||
assert.deepStrictEqual(efFlatten({ show_in_hplmn: true }), [['Show in HPLMN', 'yes']]);
|
||||
assert.deepStrictEqual(efFlatten({ title: 'Menu!', icon: '0123' }), [['Title', 'Menu!'], ['Icon', '0123']]);
|
||||
});
|
||||
|
||||
test('efDiffData reports only differing fields; efContentDiff decodes both sides', () => {
|
||||
assert.deepStrictEqual(efDiffData({ imsi: '1', kc: 'AA' }, { imsi: '2', kc: 'AA' }), [['IMSI', '1', '2']]);
|
||||
assert.deepStrictEqual(efDiffData({ imsi: '1' }, { imsi: '1' }), []);
|
||||
const dd = efContentDiff('EF.IMSI', null, '082982608200002080', '082982608200002081');
|
||||
assert.ok(dd && dd.rows.length === 1);
|
||||
assert.strictEqual(dd.rows[0][0], 'IMSI');
|
||||
assert.strictEqual(dd.rows[0][1], '228062800000208');
|
||||
const same = efContentDiff('EF.IMSI', null, '082982608200002080', '092982608200002080');
|
||||
assert.ok(same && same.sameDecoded);
|
||||
assert.strictEqual(efContentDiff('EF.NOPE', null, '00', '01'), null);
|
||||
});
|
||||
|
||||
test('efDataSummary produces one-line summaries', () => {
|
||||
assert.strictEqual(efDataSummary(efDecImsi(efBytes('082982608200002080'))), 'IMSI 228062800000208');
|
||||
assert.strictEqual(efDataSummary(efDecIccid(efBytes('988812010000400310f0'))), 'ICCID 8988211000000430010');
|
||||
assert.strictEqual(efDataSummary(efDecSume(efBytes('05054D656E7521FFFFFFFF'))), 'Menu!');
|
||||
assert.strictEqual(efDataSummary({ plmns: [{ mcc: '262', mnc: '01' }] }), '262-01');
|
||||
});
|
||||
|
||||
test('file manager decoded view renders fields, not JSON', () => {
|
||||
const out = pysimFsDecodedHtml({ name: 'EF.IMSI', fid: null }, { success: true, data: '082982608200002080' });
|
||||
assert.match(out, /IMSI/);
|
||||
assert.match(out, /228062800000208/);
|
||||
assert.match(out, /pySim JSON \(server\)/);
|
||||
assert.doesNotMatch(out, /\{"imsi"/);
|
||||
// no decoder -> raw fallback with a note
|
||||
const raw = pysimFsDecodedHtml({ name: 'EF.NOPE', fid: 'abcd' }, { success: true, data: '0011' });
|
||||
assert.match(raw, /No decoder for this file/);
|
||||
assert.match(raw, /0011/);
|
||||
});
|
||||
|
||||
test('profilerRenderReport shows decoded field diffs and the raw-only note', () => {
|
||||
const res = {
|
||||
path: 'ADF.USIM/6F07', name: 'EF.IMSI', status: 'fail',
|
||||
checks: [{ label: 'content', expected: '082982608200002080', actual: '082982608200002081', ok: false }],
|
||||
};
|
||||
const out = profilerRenderReport([res], null);
|
||||
assert.match(out, /Decoded comparison/);
|
||||
assert.match(out, /IMSI/);
|
||||
assert.match(out, /228062800000208/);
|
||||
assert.match(out, /228062800000218/);
|
||||
const same = profilerRenderReport([{
|
||||
path: 'ADF.USIM/6F07', name: 'EF.IMSI', status: 'fail',
|
||||
checks: [{ label: 'content', expected: '082982608200002080', actual: '092982608200002080', ok: false }],
|
||||
}], null);
|
||||
assert.match(same, /Decoded values match/);
|
||||
});
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pysim-simple-server"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||
requires-python = ">=3.8"
|
||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||
|
||||
@@ -21,7 +21,7 @@ from osmocom.construct import GsmOrUcs2Adapter
|
||||
from osmocom.tlv import BER_TLV_IE
|
||||
|
||||
|
||||
VERSION = '2.3.1'
|
||||
VERSION = '2.4.0'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user