Back to skill

Security audit

Douyin Comment FAQ Stills

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its FAQ-image purpose, but it requests broad unrelated Beatra account permissions and can silently replace its own executable files.

Install only if you are comfortable giving this Beatra connection broad media/account authority and allowing the package to silently update itself. Disable automatic updates with `python3 scripts/mcp_client.py update --auto off` before use if you need review before code changes, and revoke the Beatra device authorization when you no longer use the skill.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:31
Finding
Overprivileged OAuth Scope and Unrestricted MCP Tool Invocation## Vulnerability Details **File Location**: `scripts/authorize.py:31-34`; `scripts/mcp_client.py:1463-1480` **Vulnerability Type**: Excessive authorization scope and missing client-side tool allowlist **Risk Level**: High ### Vulnerable Code `scripts/authorize.py:31-34`: ```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" ) ``` `scripts/mcp_client.py:1463-1480`: ```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 functionality centers on public Douyin comment lookup, uploading user-selected assets, generating or editing FAQ images, reading task results, and checking billing information. The authorization request nevertheless includes unrelated capabilities for generating videos, music, and speech, reading and writing voices, spending wallet credits, and cancelling tasks. This violates least privilege because possession of the shared bearer credential grants materially broader account capabilities than the Skill requires. The bundled client compounds the issue by accepting an arbitrary MCP tool name from the command l ...[truncated 1599 chars]
Remediation
## Remediation Suggestions 1. Replace the broad scope with the smallest server-supported package-specific scope set needed for: - Douyin comment lookup. - Image generation, transformation, and editing. - Explicit asset uploads. - Task result reads. - Read-only model, wallet balance, and ledger access. 2. Remove video, music, speech, voice-write, general wallet-spend, and task-cancellation permissions unless a documented workflow specifically requires them. 3. Add a hardcoded client-side allowlist of permitted MCP tool names and reject every other name before creating a session or sending a request. 4. Separate read-only and paid operations where the authorization system supports separate grants. 5. Require explicit, operation-specific user approval for destructive actions such as task cancellation. 6. Prefer server-issued tokens restricted to the package identity, permitted tool names, and relevant resource types. 7. Add regression tests verifying that unrelated tool names and excessive scopes are rejected.

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Automatic Retrieval and Replacement of Executable Skill Code## Vulnerability Details **File Location**: `scripts/mcp_client.py:516-522`, `scripts/mcp_client.py:969-1017`, `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Default-on remote code update channel **Risk Level**: High ### Vulnerable Code `scripts/mcp_client.py:516-522`: ```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 ``` `scripts/mcp_client.py:969-1017`: ```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 ...[truncated 3876 chars]
Remediation
## Remediation Suggestions 1. Disable automatic updates by default and require explicit user consent before downloading or installing each new release. 2. Display the current version, proposed version, manifest digest, changed file list, and executable-file changes before installation. 3. Sign release manifests with an offline-protected signing key and embed only the corresponding public verification key in the audited client. 4. Verify the signature independently of HTTPS and CDN-provided hashes. 5. Support key rotation through a separately authenticated, auditable process rather than ordinary discovery metadata. 6. Require renewed review or approval whenever scripts, authentication logic, update logic, or Skill instructions change. 7. Consider limiting automatic updates to non-executable data; require explicit approval for Python scripts and `SKILL.md`. 8. Preserve the existing path, ownership, archive, size, locking, backup, and rollback protections, as these are useful defense-in-depth controls. 9. Clearly notify users after any update and provide a verified rollback mechanism to the previous signed version.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (15)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill declares no permissions while instructing use of shell execution, local file inspection/upload, network access, and package update behavior. This undermines user and platform visibility into what the skill can do, increasing the risk of unexpected data access or remote actions under the guise of a simple content-generation workflow.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The published purpose is a Douyin comment-to-FAQ graphics workflow, but the skill also encompasses authentication, persistent credential handling, remote tool execution, arbitrary local file upload, telemetry/registration, and package lifecycle management. That mismatch can mislead users into authorizing a much broader trust boundary than expected, enabling unintended data exfiltration, account actions, and host modification.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
Balance/ledger queries and self-management features are unrelated to the stated FAQ-still generation task, expanding the operational scope beyond user expectations. Even if read-only in part, these features expose financial metadata and normalize additional privileged operations inside a content skill, which increases attack surface and confusion about what the skill may access.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The skill includes automatic code download and installation during normal runtime, which is system-modifying behavior unrelated to creating FAQ stills. Any mechanism that can replace package-owned files introduces a supply-chain and persistence risk, especially when it can occur silently and without separate approval at the moment of change.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill requests a very broad OAuth scope set, including artifacts, videos, music, speech, voice management, task control, and wallet spending, while the stated purpose is limited to turning Douyin comments into FAQ stills. Over-scoped credentials violate least privilege and substantially increase blast radius if the token is misused, intercepted, or the skill behaves unexpectedly.

Context-Inappropriate Capability

Critical
Confidence
98% confidence
Finding
The scope includes videos:generate, music:generate, speech:generate, voices:read, and voices:write, which are not aligned with producing static FAQ stills from comments. These excess capabilities create unnecessary access to powerful media and voice functions, increasing risk of abuse and making any credential theft more damaging.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The scope includes videos:generate, music:generate, speech:generate, voices:read, and voices:write, which are not aligned with producing static FAQ stills from comments. These excess capabilities create unnecessary access to powerful media and voice functions, increasing risk of abuse and making any credential theft more damaging.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
This client contains a full self-update system that downloads manifests and archives, verifies them, and rewrites files under the local installation root. For a skill described as turning Douyin comments into FAQ stills, local package mutation is unrelated and expands the trust boundary to remote infrastructure; a compromised update channel or publisher account would let new code be installed and executed later.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The client records local skill inventory, installation references, platform data, and registration telemetry that are not necessary for comment-to-FAQ generation. This creates unnecessary metadata collection and persistence, increasing privacy risk and enabling tracking of installations and usage across environments.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The code fingerprints the host environment using environment variables and persisted host metadata, then attaches that platform identity to tool calls and registration events. For a media/FAQ generation skill, this is unnecessary contextual collection that can be used for device profiling and cross-context tracking.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill normalizes silent automatic installation as routine runtime behavior without clearly warning users up front in the skill description. This weakens informed consent for code changes on the host and makes a sensitive capability easier to hide inside an otherwise simple media workflow.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation states that the bundled client automatically performs a registration call on first use and writes a local cache file, but it does not give an explicit user-facing warning or consent mechanism for this telemetry-like behavior. Even though the data is described as non-secret and non-billable, automatic outbound transmission of package/environment identifiers and local filesystem modification can violate user expectations, privacy requirements, or restricted-environment policies.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The helper collects the local hostname and sends it in the device authorization request without an explicit user notice or consent prompt. Hostnames often reveal personal names, organization names, or internal asset identifiers, so transmitting them unnecessarily creates avoidable privacy and reconnaissance risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
maybe_auto_update() performs silent best-effort updates during ordinary execution and can modify package files without a contemporaneous user-facing prompt. Even with checksum validation, this behavior allows code changes outside the user's immediate awareness, which is especially risky in a skill whose stated purpose is unrelated to software maintenance.

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 update command provides built-in self-modification capability for the installed skill package. In the context of a simple comment-to-FAQ skill, embedding code-update functionality is unnecessarily powerful and creates a durable path for local code replacement if the upstream release process or distribution channel is ever compromised.

Static analysis

No suspicious patterns detected.