From e4ac80fb34cb191e7a9ee44e17dbbda52e4c8f09 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: Wed, 26 Aug 2026 23:23:56 +0300 Subject: [PATCH] Decode EF_IMPU/IMPI/DOMAIN empty records as empty, not text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _decode_nai (decode.py) only treated an all-0xFF record as empty. A record that is a lone identity tag, or a tag with a zero-length value (e.g. 80 00 + FF erase filler — a standard unused EF_IMPU record) slipped through to an ascii fallback and rendered as garbage text (the "Text: �" artifact). - Treat a lone tag (80 / 81) or tag+zero-length-value (80 00 / 81 00) as empty. - Only decode the TLV *value* bytes (UTF-8); never feed the TLV header or trailing FF filler to the text decoder. Drop the fragile raw.rstrip(...).decode('ascii','replace') fallback. Tests: +4 (empty 80 00+FF record, all-FF, real SIP URI, lone tag). Backend-only; stays on 1.22.0. --- simtrace2_pysniff/server/decode.py | 23 +++++++++++++++-------- tests/test_decode.py | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/simtrace2_pysniff/server/decode.py b/simtrace2_pysniff/server/decode.py index bd6bd05..a4c258d 100644 --- a/simtrace2_pysniff/server/decode.py +++ b/simtrace2_pysniff/server/decode.py @@ -3370,19 +3370,26 @@ def _decode_psloci(raw, p1=None): def _decode_nai(raw, p1=None): - """ISIM identity files (EF_IMPI/DOMAIN/IMPU): BER-TLV tag 0x80 + ASCII value.""" + """ISIM identity files (EF_IMPI/DOMAIN/IMPU): BER-TLV tag 0x80/0x81 + value. + + An erased or unused record is all-0xFF, or a lone identity tag, or a + tag with a zero-length value (e.g. ``80 00`` + FF filler) — these are + reported as empty rather than decoded as text. Only the TLV *value* + bytes are decoded (UTF-8 for a SIP/TEL URI); the TLV header and any + trailing erase filler are never fed to the text decoder. + """ if not raw or all(b == 0xff for b in raw): return {'empty': True} + data = bytes(raw).rstrip(b'\xff') # drop erase filler + if not data or (data[0] in (0x80, 0x81) and len(data) <= 2): + return {'empty': True} # lone tag, or tag + zero-length value = unused texts = [] - for tag, _length, value in parse_tlv(raw): - if tag == 0x80: - txt = value.decode('utf-8', 'replace').rstrip('\xff') - if txt: - texts.append(txt) + for tag, _length, value in parse_tlv(data): + if tag in (0x80, 0x81) and value: + texts.append(value.decode('utf-8', 'replace')) if texts: return {'text': texts[0] if len(texts) == 1 else ', '.join(texts)} - txt = raw.rstrip(b'\xff').decode('ascii', 'replace').strip() - return {'text': txt} if txt else {'raw': raw.hex().upper()} + return {'raw': raw.hex().upper()} def _decode_epsnsc(raw, p1=None): diff --git a/tests/test_decode.py b/tests/test_decode.py index f32a2b0..062ed7e 100644 --- a/tests/test_decode.py +++ b/tests/test_decode.py @@ -1017,6 +1017,31 @@ class TestFileDecoders(unittest.TestCase): f = _decode_file_data('6f05', b'enru') self.assertEqual(f['languages'], ['en', 'ru']) + def test_ef_impu_empty_record(self): + # EF_IMPU record that is an unused identity: tag 0x80, zero-length + # value, FF erase filler. Must be reported empty, NOT decoded as text. + from simtrace2_pysniff.server.decode import _decode_file_data + f = _decode_file_data('6f04', bytes.fromhex('8000' + 'ff' * 73)) + self.assertIn('empty', f) + self.assertNotIn('text', f) + + def test_ef_impu_all_ff(self): + from simtrace2_pysniff.server.decode import _decode_file_data + f = _decode_file_data('6f04', b'\xff' * 75) + self.assertIn('empty', f) + + def test_ef_impu_sip_uri(self): + from simtrace2_pysniff.server.decode import _decode_file_data + payload = b'\x80' + bytes([len(b'sip:alice@example.com')]) + b'sip:alice@example.com' + f = _decode_file_data('6f04', payload) + self.assertEqual(f.get('text'), 'sip:alice@example.com') + + def test_ef_impu_lone_tag(self): + # A record containing only the identity tag (no length/value) is empty. + from simtrace2_pysniff.server.decode import _decode_file_data + f = _decode_file_data('6f04', b'\x80') + self.assertIn('empty', f) + def test_adn(self): from simtrace2_pysniff.server.decode import _decode_file_data f = _decode_file_data('6f3a', bytes.fromhex(