feat: CAP memory estimation with a confirm step before install (v3.5.8)
Selecting a .cap in the RAM installer or the SCP81 "Install from .cap" template now runs a read-only analysis (POST /api/cap-info) before any APDU is built: the archive is validated structurally (a corrupt or wrong-format file fails here) and the bundled capmem analyzer estimates the code size and the persistent (NVRAM) / volatile (RAM) requirements, with the tool's suggested C6/C7/C8 quotas shown as information. The form's action button (Execute / Generate) stays disabled until the analysis succeeds - pressing it is the user's confirmation to continue. - pysim_simple_server/capmem.py: bundled analyzer (component parsers + JCVM opcode table + method-bytecode allocation scan), adapted to take the CAP archive as bytes and return report/memory dicts; output verified byte-identical to the workspace tool on 21 real CAPs. - _cap_info_body + POST /api/cap-info (read-only; the install endpoints stay unchanged and self-sufficient). - PWA: shared capAnalyzeFile/capMemHtml/capGateOk helpers, estimate box under both CAP inputs (reusing the idle #ram-cap-info div, new #scripts-cap-info), data-cap-gate gating in pysimApplyAvailability, stale-response guard, Retry, EN/RU strings. - Tests: tests/test_cap_memory.py (synthetic CAPs: new/newarray/ makeTransientByteArray/static fields/unknown-opcode warnings/corrupt input), frontend capmem.test.js (renderer, gate, analyze flow). - help EN/RU, docs/api.md, AGENTS; version 3.5.8; sw cache simple-v261. 567 frontend / 429 Python green.
This commit is contained in:
Executable
+1831
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@ from pysim_simple_server import netsim
|
||||
from pysim_simple_server import netstate
|
||||
from pysim_simple_server import scp81
|
||||
from pysim_simple_server import esim
|
||||
from pysim_simple_server import capmem
|
||||
from smartcard.CardMonitoring import CardMonitor, CardObserver
|
||||
from cmd2.exceptions import CommandSetRegistrationError
|
||||
|
||||
@@ -29,7 +30,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.5.7'
|
||||
VERSION = '3.5.8'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -2412,6 +2413,30 @@ def _scp81_gen_install(body):
|
||||
'module_aid': module_aid}
|
||||
|
||||
|
||||
def _cap_info_body(body):
|
||||
"""Validate a .cap archive and estimate its memory requirements.
|
||||
|
||||
Read-only: runs the same structural parse as the install paths (so a
|
||||
corrupt/wrong file fails here first) plus the capmem analyzer. Never
|
||||
touches the card, the SCP81 listener or the scripts; the estimate is
|
||||
informational - the install paths stay self-sufficient."""
|
||||
body = body or {}
|
||||
cap_hex = re.sub(r'\s', '', body.get('cap_hex') or '')
|
||||
if not cap_hex:
|
||||
return {'ok': False, 'error': 'No cap_hex provided'}
|
||||
try:
|
||||
loadfile_aid, module_aid, loadfile_data = _cap_parse(cap_hex)
|
||||
except Exception as e:
|
||||
return {'ok': False, 'error': 'cap parse failed: %s' % e}
|
||||
try:
|
||||
report, memory = capmem.analyze_bytes(bytes.fromhex(cap_hex))
|
||||
info = capmem.memory_json(report, memory)
|
||||
except Exception as e:
|
||||
return {'ok': False, 'error': 'cap analysis failed: %s' % e}
|
||||
return {'ok': True, 'load_file_aid': loadfile_aid, 'module_aid': module_aid,
|
||||
'load_file_bytes': len(loadfile_data) // 2, 'memory': info}
|
||||
|
||||
|
||||
def _scp81_bip_control(body):
|
||||
global _SCP81_LISTENER, _SCP81_PSKS, _SCP81_PSK_LEGACY
|
||||
global _SCP81_MODE, _SCP81_TARGET
|
||||
@@ -5285,6 +5310,15 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
resp = {'ok': False, 'error': str(e)}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/cap-info':
|
||||
body = self._read_body()
|
||||
self._log_req(body)
|
||||
try:
|
||||
resp = _cap_info_body(body)
|
||||
except Exception as e:
|
||||
resp = {'ok': False, 'error': str(e)}
|
||||
self._send_json(resp)
|
||||
self._log_resp(resp)
|
||||
elif self.path == '/api/scp81/log-clear':
|
||||
body = self._read_body()
|
||||
self._log_req(body)
|
||||
|
||||
Reference in New Issue
Block a user