Back to skill

Security audit

Creator Drop Talking Clips

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real Beatra talking-video workflow, but it asks for broad account authority and can silently replace its own package files after installation.

Review before installing. This skill needs selected media uploads and paid Beatra generation, but it also stores a broad shared Beatra token, can spend credits through authorized operations, records installation metadata, and silently updates itself unless disabled with its documented update --auto off control.

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 (5)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Remote Package Replacement Before Normal Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:31-32`, `scripts/mcp_client.py:969-1015`, `scripts/mcp_client.py:1542-1544`; documented in `SKILL.md:212-225` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/creator-drop-talking/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/creator-drop-talking/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=get_bytes) if not checked["update_available"]: return False _ensure_owned_baseline( install_root=resolved ...[truncated 2643 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic updates by default. 2. Require explicit, informed approval before downloading and installing each release. 3. Display the current version, proposed version, publisher identity, and affected files before replacement. 4. Sign release metadata with a publisher key pinned in the audited client, and verify the signature before trusting hashes. 5. Prefer immutable versions installed through the host package manager rather than self-modifying package code. 6. Do not perform an update immediately before an unrelated business operation. 7. Preserve existing archive, path, ownership, size, rollback, and redirect protections as defense-in-depth. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
OAuth Credential Requests Capabilities Beyond the Declared Workflow<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-37`, with exact-scope enforcement at `scripts/authorize.py:207-232` and `scripts/authorize.py:510-514` **Vulnerability Type**: Excessive authorization scope **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 authorization response is required to return that complete scope: ```python scope = _required_string(polled, "scope") if set(scope.split()) != set(SCOPE.split()): raise RuntimeError("Beatra authorization returned an unsupported scope") ``` ### Technical Analysis The talking-clip workflow legitimately needs functions such as authorized media upload, voice selection or cloning, speech synthesis, video generation, task status access, and limited billing information. The requested credential also grants unrelated or broadly expressed capabilities, including: - `images:generate` - `music:generate` - `wallet:spend` - `tasks:cancel` - general `mcp:tools` access The exact-scope checks make this broad permission set mandatory rather than allowing a narrower token. This violates least privilege and increases the authority available to any compromised client, malicious update, or incorrectly directed tool call. ### Attack Path 1. The user authorizes the talking-video Skill. 2. The authorization helper requests and stores a bearer token with the full hard-coded scope. 3. A malicious update, compromised process, or unintended caller obtains use of the bundled client. 4. The token is used for unrelated generation, wallet spending, or task cancellation operations. 5. The remote service accepts the operation because the credential already includes the relevant capability. ### Impact Assessment The credential can authorize operations outside the declared still-to-talking-video workflow. ...[truncated 266 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a package-specific minimum scope for upload, voice access, speech synthesis, video generation, and necessary task reads. 2. Remove `images:generate` and `music:generate` from this Skill. 3. Replace broad `wallet:spend` authority with server-enforced operation-specific spending permissions and limits. 4. Restrict cancellation to tasks created by this package and require an explicit user request. 5. Restrict artifact and task reads to objects associated with the current installation or package. 6. Accept narrower server-issued scopes instead of requiring exact equality with a global broad scope. 7. Use separate read-only and paid-operation credentials where the service architecture permits. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/mcp_client.py:1463
Finding
Client Forwards Arbitrary MCP Tool Names Without a Local Allowlist<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1463-1483`, `scripts/mcp_client.py:1487-1491` **Vulnerability Type**: Unrestricted privileged tool dispatch **Risk Level**: High ### Vulnerable Code ```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}, ) ``` ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` ### Technical Analysis The command-line caller controls `tool_name`, and the client forwards it directly through `tools/call`. There is no executable allowlist limiting calls to the operations documented for this Skill. Skill text recommends particular tools, but textual guidance is not an access-control boundary. Combined with the broad bearer credential, the client operates as a general privileged MCP proxy rather than a narrowly scoped talking-video client. ### Attack Path 1. A caller invokes `scripts/mcp_client.py call` with an unrelated MCP tool name. 2. Arbitrary JSON arguments are supplied through standard input. 3. The client initializes a session using the shared bearer credential. 4. It forwards the caller-selected tool name and arguments without local authorization checks. 5. The server executes the tool if it is permitted by the broad credential. The c ...[truncated 489 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a strict local allowlist containing only the tools required by this Skill. 2. Reject unknown tool names before opening an authenticated session. 3. Split read-only, upload, paid-generation, wallet, and cancellation operations into distinct commands. 4. Validate each tool's input schema locally, including permitted model capabilities and media types. 5. Require explicit user confirmation for every paid operation and destructive cancellation. 6. Enforce equivalent package-specific authorization on the server; client-side checks alone are not sufficient. 7. Avoid exposing a generic `call <tool_name>` interface in the production Skill package. ]]>

other

Warning
Location
scripts/authorize.py:340
Finding
Hostname and Agent-Environment Metadata Are Collected and Transmitted<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:340-370`, `scripts/authorize.py:453-468`; recurring platform attribution at `scripts/mcp_client.py:1218-1229` and registration at `scripts/mcp_client.py:1367-1382` **Vulnerability Type**: Environment metadata collection **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 resulting values are included in the authorization request: ```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 = pos ...[truncated 1734 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain explicit consent before transmitting a hostname or Agent-platform identifier. 2. Use a random, user-editable device label instead of the system hostname by default. 3. Make installation and per-call telemetry opt-in. 4. Avoid attaching platform attribution to every business operation unless it is operationally necessary. 5. Document the collected fields, purpose, retention period, correlation identifiers, and deletion procedure. 6. Apply restrictive permissions to `host.json` consistently, as is already done for the credential file. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:231
Finding
MCP-Controlled Upload URL Is Not Restricted to Trusted Storage Domains<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:231-264` **Vulnerability Type**: Insufficient validation of server-directed data destination **Risk Level**: Medium ### Vulnerable Code ```python def _complete_upload( result: dict[str, Any], *, mime_type: str, content: bytes, put_bytes: PutBytes, ) -> dict[str, str]: structured = result.get("structuredContent") instruction = structured.get("upload") if isinstance(structured, dict) else None if not isinstance(instruction, dict) or instruction.get("method") != "PUT": raise RuntimeError("Beatra upload instructions are invalid") url = instruction.get("url") headers = instruction.get("headers") if not isinstance(url, str) or not isinstance(headers, dict): raise RuntimeError("Beatra upload instructions are invalid") parsed = urllib.parse.urlsplit(url) if ( parsed.scheme != "https" or not parsed.hostname or parsed.username is not None or parsed.password is not None or parsed.fragment ): raise RuntimeError("Beatra upload instructions are invalid") if not all(isinstance(key, str) and isinstance(value, str) for key, value in headers.items()): raise RuntimeError("Beatra upload instructions are invalid") content_type = _header_value(headers, "Content-Type") content_length = _header_value(headers, "Content-Length") if content_type != mime_type or content_length != str(len(content)): raise RuntimeError("Beatra upload instructions are invalid") response = put_bytes(url, dict(headers), content) artifact_id = response.get("artifact_id") if not isinstance(artifact_id, str) or not artifact_id: raise RuntimeError("Beatra upload returned an invalid response") return {"type": "artifact", "artifact_id": artifact_id} ``` ### Technical Analysis The MCP response controls the upload URL and request headers. The client validates HTTPS, ...[truncated 1342 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allowlist the documented Beatra and object-storage upload domains. 2. If dynamic storage domains are necessary, validate a cryptographically signed upload grant binding the destination URL, object identifier, MIME type, byte length, expiry, and HTTP method. 3. Reject IP-literal, local, loopback, private-network, link-local, and metadata-service destinations. 4. Display the destination organization or hostname before uploading to any domain outside a predefined trusted set. 5. Minimize server-supplied headers and reject sensitive or unnecessary header names. 6. Retain the existing HTTPS, redirect, MIME, length, regular-file, no-follow, and file-stability protections. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exercises powerful capabilities (shell, network, file read/write, environment access) but does not declare them up front, which undermines informed consent and policy enforcement. In this skill’s context that is especially risky because it uploads local files, stores credentials, invokes remote services, and can modify local package files via the bundled client, creating a larger trust boundary than the user-facing description suggests.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is presented as a narrow media-generation workflow, but the referenced behavior includes OAuth login flows, persistent credential storage, generic remote tool invocation, telemetry/registration, uninstall logic, and automatic package replacement. That mismatch is dangerous because users may authorize a seemingly simple content skill without realizing it can establish long-lived access, exfiltrate local media, contact external services, and change local code over time.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
This helper provisions a shared OAuth credential and requests a broad set of account capabilities that go well beyond the stated purpose of creating talking teaser clips from still images. In the context of a narrowly scoped media-creation skill, bundling generalized account authorization expands blast radius substantially if the skill, host, or stored credential is abused.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The scope string includes unrelated permissions such as wallet:spend, music:generate, task read/cancel, and broad artifact access, none of which are justified by a one-photo talking clip workflow. If this token is compromised or the skill is misused, an attacker could spend funds or access/manipulate unrelated account resources far outside the user's expected consent.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The code records host platform, device name, and local install paths into host.json and skills.json, which is unrelated to generating drop-video clips and increases collection of local environment metadata. While not directly code-execution dangerous, it creates unnecessary privacy exposure and can aid profiling or follow-on targeting if the local state directory is accessed.

Description-Behavior Mismatch

High
Confidence
94% confidence
Finding
The file contains extensive self-update and package-management logic unrelated to the advertised one-photo talking-clip purpose. Even though the updater includes several integrity checks, it still introduces a powerful remote code modification path that can replace installed package files, materially expanding the attack surface and creating a supply-chain risk if the vendor infrastructure, discovery document, or release pipeline is compromised.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The skill performs installation telemetry and maintains a local inventory of installed skills despite being described as a clip-generation tool. This unnecessary collection and persistence of usage/install metadata increases privacy risk and can aid tracking or profiling across environments, especially because it occurs automatically and best-effort on normal operations.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code fingerprints the host environment by inspecting environment variables and reading host.json to classify the agent platform. For a media-creation skill, this exceeds apparent functional need and creates unnecessary environment reconnaissance that could support user tracking, behavioral segmentation, or platform-targeted behavior.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
This uninstall script can revoke the shared Beatra device authorization and remove global state under ~/.beatra, which affects all installed skills, not just this package. That is a powerful cross-skill capability that exceeds the apparent scope of a single creator-video skill and can cause denial of service for other skills if the inventory is wrong, tampered with, or incomplete.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
The code enumerates shared skill inventory and deletes shared state files, giving this package visibility into and influence over other installed skills. Even though the logic is cautious, a single skill should not independently control cross-skill state because corruption, stale metadata, or crafted inventory entries could lead to unintended removal of platform-wide connectivity.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that the bundled client silently installs newer releases automatically without separate confirmation and replaces package-owned files. Silent self-update is a supply-chain and local-integrity risk: if the update channel, signing, or publisher account is compromised, the skill can change behavior after approval without renewed user consent.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The document explicitly states that the client silently checks for updates and automatically installs a newer release without separate confirmation. Even with checksum, path, and rollback protections, silent self-updating changes executable code on the user's system without an explicit approval step, which increases supply-chain risk and reduces user control if the update channel, signing process, or upstream infrastructure is ever compromised.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation states that the client automatically performs an installation registration call and writes a local cache file, but it does not clearly warn users up front that metadata will be transmitted or that a file will be created in the home directory. Even if the data is described as non-secret and non-billable, silent telemetry-style behavior can violate user expectations, privacy requirements, or enterprise policy in sensitive environments.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The authorization flow requests wallet spending permission but the user-facing console messages only describe signing in and selecting Allow, without clearly warning about financial authority. This creates a consent mismatch where users may approve billing or spending capability they do not reasonably expect from a media clip generation skill.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill can silently auto-update itself and replace installed files during normal execution without user-facing warning or approval. Silent code modification is dangerous because it lets future behavior change outside the reviewed artifact, undermines trust boundaries, and magnifies any compromise of the upstream distribution channel.

Credential Access

High
Category
Privilege Escalation
Content
scope = _required_string(polled, "scope")
            if set(scope.split()) != set(SCOPE.split()):
                raise RuntimeError("Beatra authorization returned an unsupported scope")
            credential_path = state_dir / "credentials.json"
            _atomic_json(
                credential_path,
                {
Confidence
88% confidence
Finding
This code writes a bearer access token to a plaintext JSON file under the user's home directory, creating a reusable local secret outside an OS-backed credential vault. Although POSIX permissions are tightened, compromise of the user account, backups, or insecure endpoint tooling could expose a broad-scope token with wallet and resource access.

Credential Access

High
Category
Privilege Escalation
Content
#: these and then removes the directory only if it is empty — the script
#: never recursively deletes a directory it does not fully understand.
_STATE_FILES = (
    "credentials.json",
    "installation.json",
    "host.json",
    "skills.json",
Confidence
90% confidence
Finding
The script explicitly targets credentials.json as part of removable shared state, indicating access to authentication material for the device connection. In context this appears intended for uninstall cleanup rather than theft, but allowing a content-generation skill package to touch shared credentials increases blast radius and creates an unnecessary path to credential misuse or service disruption.

Credential Access

High
Category
Privilege Escalation
Content
def _device_token(state_dir: Path) -> str | None:
    path = state_dir / "credentials.json"
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, ValueError):
Confidence
93% confidence
Finding
This function reads the bearer access token from shared credentials.json and uses it to call the revocation endpoint. While the immediate use is revocation, direct token access by a non-platform skill is dangerous because any code path with token visibility could be repurposed or abused to exfiltrate or misuse credentials, and here it also enables platform-wide disconnects.

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 exposed update command enables the package to modify its own installed code, which is a self-modification capability inappropriate for the stated clip-generation function. Self-modifying software substantially increases supply-chain and persistence risk because reviewed code can later be replaced by remotely fetched content.

Static analysis

No suspicious patterns detected.