Back to skill

Security audit

Assembly One-Step Clips

Security checks for vulnerabilities and agentic risk

Overview

This skill is coherent for making paid assembly-step videos, but it asks for broader account authority than the stated task needs and can silently replace its own package code through automatic updates.

Install only if you are comfortable granting Beatra a persistent shared device credential with spending authority and broader media capabilities than this single video skill needs. Before use, consider disabling automatic updates with the documented update --auto off command, review Beatra account activity and credit use, and avoid approving or running unexpected MCP tool calls outside the listed assembly-video 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
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Self-Update Permits Remotely Controlled Code Replacement<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1020`, `scripts/mcp_client.py:1542-1544`; related update trust validation at `scripts/mcp_client.py:299-328` and `scripts/mcp_client.py:469-491` **Vulnerability Type**: Silent remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code ```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=get_bytes) if not checked["update_available"]: return False _ensure_owned_baseline( install_root=resolved_root, update_home=update_home, get_bytes=get_bytes, ) discovery = checked["discovery"] manifest, new_files = download_upda ...[truncated 4211 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Permit silent update checks, but require explicit user approval before replacing code. 2. Sign discovery metadata and release manifests with an offline-controlled package-signing key. 3. Embed or securely pin the corresponding public key in the reviewed package. 4. Verify the signature before trusting the version, URLs, hashes, package identity, channel, or locale. 5. Implement signed rollback protection so a compromised endpoint cannot advertise an older vulnerable release. 6. Separate checking, downloading, and installation into explicit stages with clear user-visible status. 7. Preserve the existing path validation, size restrictions, owned-file checks, transactional replacement, and rollback safeguards. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
Video Skill Receives Broad Cross-Media Spending Authority and Allows Arbitrary MCP Tool Dispatch<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-37`; unrestricted dispatch at `scripts/mcp_client.py:1463-1481` **Vulnerability Type**: Excessive authorization and missing local tool allowlist **Risk Level**: High ### Vulnerable 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" ) ``` The command accepts and forwards any caller-supplied tool name: ```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 purpose is to upload assembly stills and create image-to-video clips. Nevertheless, authorization requests permissions for music generation, speech generation, voice reading and writing, image generation, broad artifact operations, task cancellation, and wallet spending. Some permissions are necessary for the documented workflow, including video generation, upload/artifact creation, model discovery, task reads, and potentially user-requested cancellation and wallet reporting. The unrelated music, speech, voice, and general image-generation capabilities exceed the minimum privileges required by this package. The bundled client also accepts an arbitrary ...[truncated 1454 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Issue package-specific credentials restricted to the exact capabilities needed by this Skill. 2. Remove music, speech, voice-read, voice-write, and unrelated image-generation scopes. 3. Separate wallet reads from wallet spending and avoid granting spending authority until a confirmed paid operation is submitted. 4. Enforce an exact local allowlist, such as: - `beatra.models.list` - `beatra.assets.upload` - `beatra.videos.animate` - required task status/list operations - user-confirmed task cancellation - documented read-only wallet operations - required installation registration 5. Reject all unknown tool names before initializing an authenticated request. 6. Apply operation-specific confirmation checks in code rather than relying only on prose instructions. 7. Use distinct credentials or capability grants for read-only diagnostics, paid generation, and cancellation. ]]>

other

Warning
Location
scripts/authorize.py:340
Finding
Authorization Collects and Transmits Hostname and Stable Environment Identifiers<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:340-369`, `scripts/authorize.py:455-468`; related registration at `scripts/mcp_client.py:1388-1414` **Vulnerability Type**: Environment reconnaissance and privacy exposure **Risk Level**: Medium ### Vulnerable 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] ``` The collected values are included in authorization: ```python external_reference = _installation_reference(state_dir) 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["d ...[truncated 2452 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not collect or transmit the hostname by default. 2. Use a random, non-identifying local device label unless the user explicitly supplies a recognizable name. 3. Display the exact telemetry fields before authorization and obtain informed consent. 4. Permit users to disable installation registration and source-platform attribution. 5. Prefer explicit platform input over automatic environment inspection. 6. Document retention, correlation, deletion, and revocation behavior for the stable installation reference. 7. Provide a command that removes locally stored host metadata and requests deletion of corresponding server-side telemetry. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential File Is Read Without Verifying the Required User-Only ACL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1044-1053`; related creation behavior at `scripts/authorize.py:121-135` **Vulnerability Type**: Inadequate local credential access-control validation **Risk Level**: Medium ### Vulnerable Code ```python def _read_private_credentials(state_dir: Path, path: Path) -> str: if os.name == "nt": # The state directory lives under the user profile, whose default # ACL is already private to the user (the gh/aws/gcloud posture). # The former custom DACL verification was dropped deliberately: its # command patterns read as hostile to agent safety policies and # endpoint security, failing installs while adding nothing an # elevated administrator could not bypass. return path.read_text(encoding="utf-8") if os.name != "posix": raise RuntimeError("Beatra credential permissions are unsupported on this platform") try: directory_stat = os.lstat(state_dir) if ( not stat.S_ISDIR(directory_stat.st_mode) or stat.S_IMODE(directory_stat.st_mode) != 0o700 or directory_stat.st_uid != os.getuid() ): raise RuntimeError("Beatra credential permissions are unsafe; authorize again") flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) descriptor = os.open(path, flags) try: file_stat = os.fstat(descriptor) if ( not stat.S_ISREG(file_stat.st_mode) or stat.S_IMODE(file_stat.st_mode) != 0o600 or file_stat.st_uid != os.getuid() ): raise RuntimeError("Beatra credential permissions are unsafe; authorize again") with os.fdopen(descriptor, encoding="utf-8") as handle: descriptor = -1 return handle.read() finally: if descriptor >= 0: os.close(descriptor) except RuntimeError: ...[truncated 2707 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the Windows state directory and credential file with an explicit DACL granting access only to the intended user and required system principals. 2. Disable or carefully control inherited permissions on the credential file. 3. Verify the file owner and effective ACL before every credential read. 4. Reject reparse points and other redirected filesystem objects. 5. Open the file through a Windows API that permits post-open handle validation and avoids path substitution races. 6. Fail closed with a clear remediation message if confidentiality cannot be established. 7. Consider storing the token through Windows Credential Manager or DPAPI instead of a plaintext JSON file. 8. Keep the existing strict POSIX owner, type, mode, and no-follow checks. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exposes significant capabilities including file access, shell, network, and environment use without declaring permissions or presenting them clearly to the user. That creates a trust and transparency failure: users may invoke a seemingly narrow media-conversion skill without understanding it can read local files, persist data, execute commands, and contact remote services.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented purpose is limited to generating assembly-step clips, but the referenced behavior includes OAuth login, persistent credential storage, arbitrary remote MCP tool invocation, local uploads, telemetry/registration, self-update, and uninstall/token-revocation workflows. This mismatch is dangerous because it materially broadens the trust boundary and attack surface beyond what a user would reasonably expect from the skill description, increasing the risk of credential misuse, unintended data exfiltration, and remote code or package changes.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that the bundled client silently checks for and automatically installs newer releases without separate confirmation. Even if updates are signed and constrained to official sources, silent code replacement in a tool with shell, file, network, and credential-handling capabilities is a meaningful supply-chain and user-consent risk, especially because the warning is not surfaced prominently near normal usage.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The workflow directs the agent to upload user-provided images and invoke a billable video-generation API, but the skill description does not clearly warn the user that data will be transmitted to an external service and that credits or charges may be incurred. This can lead to unauthorized network disclosure of images and unexpected billing if the agent proceeds without explicit informed consent at the skill boundary.

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
82% confidence
Finding
This client includes a self-update mechanism that downloads code and replaces files in the installed package. Although it performs substantial validation (HTTPS, pinned hostnames, manifest/archive SHA-256 checks, path validation, rollback, ownership checks), any compromise of the discovery endpoint/CDN/signing pipeline or trust root would turn this into a remote code delivery path, which is especially sensitive in an agent skill because updated code will run with the user's local privileges on future invocations.

Static analysis

No suspicious patterns detected.