Back to skill

Security audit

Hotel Amenity Clips

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its hotel-video purpose, but it deserves review because it silently self-updates executable code and uses a broad shared Beatra account token.

Review before installing. Use this only if you trust Beatra with a shared account token that can authorize more than hotel video generation, and consider disabling automatic updates with `python3 scripts/mcp_client.py update --auto off` before ordinary use. Be aware that local amenity images are uploaded, Beatra credits may be spent after explicit production approval, and local credential/state files are kept under `~/.beatra` until revoked or uninstalled according to the bundled workflow.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Default-enabled silent self-update permits remote replacement of executable Skill code## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1019`; supporting download and trust configuration at `scripts/mcp_client.py:31-32, 334-490`; automatic invocation at `scripts/mcp_client.py:1543-1544` **Vulnerability Type**: Remote payload retrieval and execution through an insufficiently authenticated update channel **Risk Level**: High ### Relevant Code ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/hotel-amenity-clip/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/hotel-amenity-clip/channels/clawhub/v{version}" ``` ```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_CHECK_MAX_AGE_SECONDS ): return False state["last_checked_at"] = observed_at _write_private_json(update_home / "state.json", state) checked = check_update(get_bytes ...[truncated 3948 chars]
Remediation
## Remediation Suggestions 1. Disable automatic code installation by default. Update checks may remain automatic, but require explicit user approval before replacing executable files. 2. Digitally sign release metadata with an offline or otherwise strongly protected release key. 3. Embed or securely pin the verification public key in the reviewed client and reject any release lacking a valid signature. 4. Use a signed metadata framework with key rotation, expiration, rollback protection, and threshold signing, such as The Update Framework. 5. Separate notification from installation so ordinary media commands never silently change the code that implements those commands. 6. Display the target version and verified release identity before installation, and provide an auditable changelog. 7. Preserve the existing archive limits, redirect rejection, path validation, ownership checks, and rollback logic; these are useful defense-in-depth but do not replace signature verification. 8. Consider distributing updates through the same reviewed package repository that delivered the original Skill rather than implementing an independent runtime updater.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/authorize.py:30
Finding
Full-scope shared bearer token and arbitrary tool dispatcher violate least privilege## Vulnerability Details **File Location**: `scripts/authorize.py:30-33`; arbitrary tool dispatch at `scripts/mcp_client.py:1458-1486` **Vulnerability Type**: Excessive OAuth authorization and unrestricted MCP tool selection **Risk Level**: Medium ### Relevant Code ```python SCOPE = ( "mcp:tools artifacts:write images:generate videos:generate music:generate " "speech:generate voices:read voices:write wallet:spend tasks:read artifacts:read tasks:cancel" ) ``` ```python def _run_command(command: str, tool_name: str | None = None) -> dict[str, Any]: session = _session_with_registration( state_dir=Path.home() / ".beatra", post_json=_default_post_json, ) if command == "tools": return session.request(2, "tools/list", {}) try: arguments = json.load(os.sys.stdin) except json.JSONDecodeError as exc: raise RuntimeError("Tool arguments on stdin must be one JSON object") from exc if not isinstance(arguments, dict): raise RuntimeError("Tool arguments on stdin must be one JSON object") assert tool_name is not None return session.request( 2, "tools/call", {"name": tool_name, "arguments": arguments}, ) ``` ### Technical Analysis The declared Skill functionality requires reading model constraints, uploading hotel amenity stills, generating videos, reading generated tasks and artifacts, optionally reading wallet information, and canceling a task only at the user's request. Instead, authorization requests a shared bearer token covering unrelated capabilities, including image generation, music generation, speech generation, voice reading and writing, general artifact access, wallet spending, and task cancellation. The bundled client also accepts an arbitrary `tool_name` and forwards it to `tools/call` without a package-specific allowlist. Consequently, restrictions in `SKILL.md` are policy gu ...[truncated 1889 chars]
Remediation
## Remediation Suggestions 1. Request only scopes needed by this package, such as model discovery, amenity-image upload, video generation, task reads, artifact reads, and narrowly defined wallet reads where requested. 2. Remove music, speech, voice-write, unrelated image-generation, and other nonessential scopes. 3. Separate read-only wallet access from wallet spending. A media-generation authorization should not imply unrestricted wallet operations. 4. Replace the shared full-scope device token with package-specific or capability-specific tokens. 5. Add a strict client-side allowlist for this Skill, including only documented operations such as: - `beatra.models.list` - `beatra.assets.upload` - `beatra.videos.animate` - `beatra.tasks.get` - `beatra.tasks.list` - `beatra.tasks.cancel` - `beatra.wallet.get` - `beatra.wallet.ledger` - `beatra.installations.register` 6. Enforce equivalent restrictions server-side; client-side checks alone can be bypassed. 7. Require explicit user confirmation for cancellation and every billable generation request. 8. Use short-lived, audience-bound tokens and support revocation at package granularity.

other

Note
Location
scripts/authorize.py:345
Finding
Authorization transmits local hostname and inspects Agent environment without a functional requirement## Vulnerability Details **File Location**: `scripts/authorize.py:345-370`; hostname is included in the device-authorization request in `scripts/authorize.py:429-442` **Vulnerability Type**: Unnecessary environment reconnaissance and device-identifying telemetry **Risk Level**: Low ### Relevant Code ```python def detect_host_platform(explicit: str | None = None) -> str: """The agent environment this process runs inside (docs/device-model.md). Order: explicit agent self-report > environment signatures > unknown. Detection reads the process environment only — nothing else runs, nothing reaches the network. """ if explicit: candidate = explicit.strip().lower().replace(" ", "-") if _PLATFORM_VALUE.fullmatch(candidate): return candidate env = os.environ if env.get("CLAUDECODE") == "1" or "CLAUDE_CODE_ENTRYPOINT" in env: return "claude-code" if any(key.startswith("CODEX_") for key in env): return "codex" ai_agent = env.get("AI_AGENT", "").lower() matched = re.match(r"([a-z0-9-]+)_", ai_agent) if matched and _PLATFORM_VALUE.fullmatch(matched.group(1)): return matched.group(1) return "unknown" def device_display_name() -> str | None: """A hostname the user will recognise in the console device list.""" try: name = socket.gethostname().strip() except OSError: return None if not name or not name.isprintable(): return None return name[:120] ``` ```python form: dict[str, str] = { "client_id": CLIENT_ID, "resource": MCP_URL, "scope": SCOPE, "platform": host_platform, "client_name": PACKAGE_DISPLAY_NAME, "external_installation_ref": external_reference, "package_version": PACKAGE_VERSION, "package_slug": PACKAGE_SLUG, } if device_name: form["device_name"] = device_name status, created = po ...[truncated 2432 chars]
Remediation
## Remediation Suggestions 1. Remove hostname collection unless it is essential to a user-requested device-management feature. 2. Use a generic local label or the existing random installation reference instead of the system hostname. 3. If a recognizable device label is desired, ask the user to provide one explicitly and make submission optional. 4. Clearly disclose the exact fields transmitted during authorization, their purpose, retention period, and deletion mechanism. 5. Minimize platform telemetry to a coarse value and provide an opt-out. 6. Do not persist `device_name` in `host.json` unless necessary; apply private atomic-file handling if it remains. 7. Ensure server-side logs and device records apply retention limits and allow users to delete associated telemetry.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares no permissions, yet its instructions clearly require file access, shell execution, network access, local state writes, and likely environment/credential handling via the bundled client. This is dangerous because users and host systems cannot accurately assess or sandbox the skill's real capabilities, increasing the chance of unintended data exposure or execution beyond expected scope.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The public description says the skill only turns existing hotel amenity photos into clips, but the body also describes OAuth login, credential storage, remote tool invocation, file uploads, telemetry/registration, uninstall-side credential revocation, and automatic self-updating. That mismatch is security-relevant because it conceals sensitive behaviors that affect trust boundaries, persistence, local state, and code integrity, making informed consent and policy enforcement much harder.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The document states that the client silently checks for updates and automatically installs newer versions by default before normal commands, without an upfront warning that this behavior modifies local files. Even though the text describes integrity checks and rollback protections, silent default-enabled self-updating increases supply-chain and user-consent risk because software replacement can occur unexpectedly during ordinary use.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation states that the client automatically performs an installation registration call and transmits package slug, version, platform, and a stable external installation reference, but it does not clearly present this as telemetry or provide an explicit user-facing warning/consent expectation. Even if the data is described as non-secret and non-billable, undisclosed automatic outbound transmission can violate user expectations, privacy requirements, or enterprise deployment policies.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The document instructs use of a single full-scope device token shared across packages and describes automatic inclusion of telemetry fields, but it does not provide an explicit warning about the privacy, authorization, and account-impact implications of that design. In a skill/package ecosystem, reusing a high-privilege credential increases blast radius: any package misuse, compromise, or user misunderstanding could lead to broader account actions than expected, even if the token itself is not printed.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The client performs silent automatic self-updates and then writes new package files into its own installation directory without any user-facing approval at execution time. Even though the code includes integrity checks, this still creates a remote code modification channel: compromise of the update service, signing/checksum pipeline, or package publisher could push new code that is executed later on the host.

Self-Modification

High
Category
Rogue Agent
Content
)
    update = subparsers.add_parser(
        "update",
        help="Check, install, or configure Beatra package self-updates",
    )
    update.add_argument(
        "--check",
Confidence
95% confidence
Finding
The package includes a self-update mechanism capable of replacing installed code and also triggers best-effort automatic updates during normal command execution. In the context of an agent skill, self-modifying behavior materially increases risk because remote infrastructure can alter future behavior on the endpoint without normal deployment review, making supply-chain compromise much more dangerous.

Static analysis

No suspicious patterns detected.