fix: CAP compatibility test review fixes (v3.6.49)

Review of v3.6.48 found six issues; the two that matter in use:

- ramProbeParse('') errored, so the basic 'is this CAP compatible with
  this card at all?' run (no overrides) was refused by an alert.  An empty
  list is valid now: the test runs with the CAP's own versions.
- ramCompatVerdict said 'Import list rejected' for every failure.  It now
  distinguishes: a request error (no steps), a failed format probe (the
  card not answering), an INSTALL [for load] refusal, and only then the
  import list - each with its own headline.

Plus:
- the result line shows the applied probe (ramCompatProbeNote: 'probe:
  7 imports -> 0.0 · +1 appended', unmatched AIDs included);
- #ram-compat-block validates 1..240 like the install's block size;
- the server reports probe_unmatched (override AIDs matching no entry,
  previously silent when another override matched) and logs them;
- tests: the endpoint's probe-400 path (a mini CAP + a dummy scc),
  _cap_import_aids, the verdict cases and the probe note.

sw cache simple-v322; tests: 540 Python / 687 Node.
This commit is contained in:
2026-09-30 08:13:12 +03:00
parent 0dc79a3ba4
commit 80c26f14f2
7 changed files with 170 additions and 25 deletions
+40 -3
View File
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.6.48'
VERSION = '3.6.49'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -870,6 +870,31 @@ def _cap_import_end(load_file_hex):
raise ValueError('no Import component in the load file')
def _cap_import_aids(load_file_hex):
"""The AIDs of the load file's Import component (upper-case hex)."""
data = bytes.fromhex(load_file_hex or '')
off = 0
while off + 3 <= len(data):
tag = data[off]
size = int.from_bytes(data[off + 1:off + 3], 'big')
if tag == 0x04:
start = off + 3
end = start + size
out = []
p = start + 1
for _ in range(data[start]):
if p + 3 > end:
break
aid_len = data[p + 2]
if p + 3 + aid_len > end:
break
out.append(bytes(data[p + 3:p + 3 + aid_len]).hex().upper())
p += 3 + aid_len
return out
off += 3 + size
return []
def _cap_compat_blocks(load_file_hex, block_size):
"""(boundary, total) LOAD block numbers for the compatibility test.
@@ -6888,9 +6913,11 @@ class PysimHandler(BaseHTTPRequestHandler):
loadfile_aid, module_aid, loadfile_data = _cap_parse(cap_hex)
probe_applied = None
probe_unmatched = []
probe = body.get('probe_imports') or {}
additions = body.get('probe_additions') or []
if probe or additions:
original_aids = set(_cap_import_aids(loadfile_data))
try:
loadfile_data, probe_applied = _probe_import_versions(
loadfile_data, probe, additions)
@@ -6899,8 +6926,17 @@ class PysimHandler(BaseHTTPRequestHandler):
self._send_json(resp, 400)
self._log_resp(resp)
return
sys.stderr.write('CAP-COMPAT: import probe applied: %s\n'
% json.dumps(probe_applied))
known = original_aids | {
str(a.get('aid', '')).replace(' ', '').upper()
for a in additions}
probe_unmatched = sorted(
k for k in probe
if str(k).lower() != 'all'
and str(k).replace(' ', '').upper() not in known)
sys.stderr.write('CAP-COMPAT: import probe applied: %s%s\n' % (
json.dumps(probe_applied),
(' (unmatched: %s)' % ', '.join(probe_unmatched))
if probe_unmatched else ''))
block_size_req = body.get('load_block_size')
if block_size_req in (None, ''):
@@ -6961,6 +6997,7 @@ class PysimHandler(BaseHTTPRequestHandler):
'boundary_block': boundary, 'total_blocks': total_blocks,
'load_file_aid': loadfile_aid, 'module_aid': module_aid,
'ram_format': ram_format, 'probe_imports': probe_applied,
'probe_unmatched': probe_unmatched,
'final_cntr': state['cntr'], 'load_block_size': block_size,
'load_block_size_requested': block_size_req}
if not ok: