Back to skill

Security audit

Douyin Hot Search Hook Clips

Security checks for vulnerabilities and agentic risk

Overview

The skill fits a Beatra media-generation workflow, but it keeps a broad shared account token and silently self-updates executable files, so it needs review before installation.

Install only if you are comfortable granting Beatra a shared, persistent device credential with media, artifact, task, wallet-spend, and voice permissions. Turn automatic updates off with the documented command if you want reviewed code to stay fixed, and avoid invoking arbitrary Beatra tool names outside the documented 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 (2)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Remote Updates Can Replace Executable Skill Code Without Publisher-Signed Authentication<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:24-25, 299-330, 469-491, 969-1019, 1539-1542`; `SKILL.md:256-272`; `references/automatic-updates-and-safety.md:3-19` **Vulnerability Type**: Silent remote payload retrieval and executable code replacement **Risk Level**: High ### Vulnerable Code ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/douyin-hot-to-hook-clip/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/douyin-hot-to-hook-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=get_bytes) if not checked["update_available"]: return False _ensure_owned_baseline( ...[truncated 4309 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Update checks may be automatic, but installation should require explicit user approval. 2. Display the current version, proposed version, release source, requested file changes, and security-relevant release notes before installation. 3. Sign release manifests with a dedicated publisher signing key and verify signatures against a public key pinned in the audited package. 4. Protect signing keys using an offline or hardware-backed release process with threshold approval and audit logging. 5. Support explicit version pinning so users can remain on an audited release. 6. Separate update checking from all business and billable commands. A normal MCP operation should not implicitly modify executable code. 7. Preserve the existing path, archive, size, ownership, downgrade, and rollback protections; these are useful defense-in-depth controls but are not substitutes for cryptographic publisher authentication. 8. Record successful and failed update events in a user-visible local audit log without storing credentials or private request content. 9. Consider distributing updates through the hosting platform’s reviewed package mechanism rather than implementing a package-local self-updater. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:31
Finding
Overprivileged Shared Device Token Is Exposed Through Unrestricted MCP Tool Dispatch<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:31-35`; `scripts/mcp_client.py:1463-1481, 1484-1490`; `references/installation-and-auth.md:73-74`; `references/mcp-connection.md:9-10` **Vulnerability Type**: Excessive OAuth scope and missing package-specific tool authorization **Risk Level**: High ### Vulnerable Code The authorization helper requests a broad, shared scope: ```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 bundled client accepts any tool name supplied by its caller: ```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") ``` The documentation explicitly describes the credential as full-scope: ```text They share the one full-scope Device Token stored in `~/.beatra/credentials.json`. ``` ### Technical Analysis The declared Skill workflow requires a limited set of operations associated with: - Douyin hot-search discovery and execution. - Media upload. - Voice selection or cloning. - Text-to-speech generation. - Image-to-video generation. - Task s ...[truncated 3192 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared full-scope token with a package-specific credential restricted to the operations required by this Skill. 2. Remove unrelated capability scopes, particularly image generation and music generation, unless a documented workflow genuinely requires them. 3. Separate read, write, spend, and cancellation permissions. Grant task cancellation only when a user explicitly requests cancellation. 4. Implement an explicit local allowlist in `_run_command()`. At minimum, restrict calls to the exact tools documented for this package, such as: - Douyin social search, inspection, and execution tools. - `beatra.assets.upload`. - Required model and voice listing operations. - Voice cloning when explicitly selected. - Speech synthesis and image-to-video animation. - Task list/get and user-requested cancellation. - Read-only wallet balance and ledger operations. 5. Reject unknown tool names before initializing an authenticated session. 6. Require a distinct authorization grant when the user elects to use an optional capability not covered by the package’s baseline workflow. 7. Bind spending authorization to a user-approved operation, price, request identity, and expiration window instead of granting unrestricted wallet spending. 8. Isolate task and artifact visibility by package or installation where supported. 9. Avoid sharing a single high-privilege credential across independent Skills. If credential sharing is unavoidable, enforce server-side package/tool constraints using the authenticated package identity rather than optional source-attribution arguments. 10. Add tests proving that unrelated tools, altered source-attribution fields, and unapproved cancellation or spending calls are rejected locally and server-side. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (15)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill drives shell commands, file inspection/upload, network access, and persistent local state, yet no explicit permissions are declared. That creates a trust gap where operators may approve a seemingly simple content-generation skill without realizing it can read local files, invoke remote services, and modify local installation state.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented purpose is narrow video generation, but the skill also performs authentication, persistent credential storage, generic remote tool invocation, file uploads, telemetry/registration, uninstall cleanup, and self-update. This mismatch increases the chance of overbroad trust and consent, letting a user authorize a creative-media workflow that actually introduces account, data, and code-supply-chain exposure.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill includes an automatic self-update path that downloads and installs new code, which is unrelated to the core task of generating Douyin hook clips. Even if updates are signed and restricted to official paths, this materially enlarges the attack surface by enabling remote code changes after initial trust is granted.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The OAuth scope string requests a very broad set of capabilities, including artifacts, music, speech, voice management, task control, and wallet spending, while the skill is described as generating short Douyin talking-hook clips from stills and trends. This violates least-privilege and means that if the skill, its backend, or its stored token is abused, an attacker could access or perform actions far beyond the advertised functionality.

Context-Inappropriate Capability

High
Confidence
90% confidence
Finding
The voices:write scope grants modification capability over account voice resources, which is broader than merely generating spoken clips. This could let the skill alter, create, or overwrite voice configurations outside the user’s expectations if the token is misused.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The voices:write scope grants modification capability over account voice resources, which is broader than merely generating spoken clips. This could let the skill alter, create, or overwrite voice configurations outside the user’s expectations if the token is misused.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The voices:write scope grants modification capability over account voice resources, which is broader than merely generating spoken clips. This could let the skill alter, create, or overwrite voice configurations outside the user’s expectations if the token is misused.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client implements a full remote self-update pipeline that downloads manifests and archives, verifies them, and replaces installed package files on disk. Even though there are integrity checks, this materially exceeds the stated purpose of generating Douyin hook clips and creates a supply-chain/self-modifying trust boundary where remote infrastructure can change local code execution behavior after installation.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill records local installation inventory and sends installation telemetry unrelated to its advertised clip-generation function. This creates unnecessary collection and persistence of host metadata, increasing privacy risk and creating side-channel visibility into what is installed and where.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The code fingerprints the host environment by inspecting environment variables and local host.json state to identify platforms such as Claude Code or Codex. This is not needed for the declared media-generation purpose and increases tracking and environment-awareness capabilities that can be abused for profiling, targeting, or conditional behavior.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The CLI accepts an arbitrary tool name and JSON arguments from stdin, then forwards them to the remote MCP server as tools/call. That makes this package a general-purpose remote tool proxy rather than a narrowly scoped Douyin hook generator, greatly expanding what actions a caller or remote backend can induce through the skill context.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill states that newer versions install automatically without separate confirmation, while the warning is embedded in the documentation rather than surfaced as a clear runtime consent step. Silent code replacement undermines informed consent and can expose users to unexpected new behavior, including expanded data access or changed remote operations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The document states that the client silently checks for and automatically installs newer releases by default before ordinary commands, without separate confirmation. Even though it describes integrity checks and rollback protections, silent self-modifying behavior materially increases supply-chain risk and removes an important user consent barrier, especially for a tool that may run in automated or privileged environments.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill performs silent automatic updates during normal execution via maybe_auto_update(), modifying installed code without a user-facing prompt at the time of change. In the context of a creative media skill, this is especially risky because users would not reasonably expect routine invocations to alter executable package contents behind the scenes.

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
97% confidence
Finding
The exposed self-update command enables the package to replace its own installed code, which is a self-modification capability. In a skill whose stated purpose is video hook generation, this is a dangerous expansion of authority because it permits post-install behavior changes driven by external package infrastructure rather than the originally reviewed code.

Static analysis

No suspicious patterns detected.