T03 · Remote Payload Retrieval and Execution
Error
- Location
- scripts/mcp_client.py:969
- Finding
- Silent unsigned self-update permits remote replacement of executable Skill code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1018` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Technical Analysis The MCP client silently checks for and installs updates before ordinary commands. Automatic updates are enabled when the state file is missing or invalid: ```python def _read_update_state(update_home: Path) -> dict[str, Any]: path = update_home / "state.json" try: value = json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return {"schema_version": 1, "auto_update": True} if not isinstance(value, dict) or value.get("schema_version") != 1: return {"schema_version": 1, "auto_update": True} return value ``` The automatic update routine retrieves mutable discovery metadata and replaces package-owned files without interactive confirmation: ```python def maybe_auto_update( *, state_dir: Path | None = None, install_root: Path | None = None, get_bytes: GetBytes = _default_get_bytes, now: float | None = None, ) -> bool: """Best-effort silent update. Never block the requested MCP command.""" resolved_state = state_dir or Path.home() / ".beatra" try: resolved_root = (install_root or _current_install_root()).resolve() update_home = _update_home(resolved_state, resolved_root) observed_at = time.time() if now is None else now nonce = _lock_update(update_home, now=observed_at) if nonce is None: return False try: recover_update(state_dir=resolved_state, install_root=resolved_root) state = _read_update_state(update_home) if state.get("auto_update", True) is False: return False last_checked = state.get("last_checked_at") if ( isinstance(last_checked, (int, float)) and observed_at - float(last_checked) < UPDATE_C ...[truncated 3850 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Permit automatic checks, but require explicit confirmation before replacing executable files. 2. Digitally sign release metadata with an offline or otherwise independently protected release key. 3. Embed the trusted public key or a pinned root of trust in the audited package and verify signatures before accepting discovery metadata, manifests, or archives. 4. Use a signed metadata framework such as TUF to provide threshold signing, rollback protection, key rotation, and compromise recovery. 5. Do not treat SHA-256 values delivered by the same mutable server as publisher authentication. 6. Display the target version, release identity, changed executable files, and signer before installation. 7. Separate content-only updates from executable script updates; require stronger consent for Python file replacement. 8. Preserve the existing path traversal, archive size, ownership, transactional replacement, and rollback protections. 9. Surface update failures and completed executable updates in an auditable local log without recording credentials or user content. ]]>
