Back to skill

Security audit

Founder IP Avatar Studio

Security checks for vulnerabilities and agentic risk

Overview

The skill appears aimed at legitimate avatar-video generation, but it needs review because it stores a broad shared Beatra credential and silently replaces its own package files through automatic updates.

Install only if you are comfortable granting Beatra a shared device authorization that can access multiple Beatra capabilities, upload selected portrait or voice files, spend credits for approved generation work, and receive silent package updates by default. Consider disabling automatic updates with the documented command and revoking the Beatra device authorization from the console when you no longer need it.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:35
Finding
Overprivileged Shared Credential with Unrestricted MCP Tool Dispatch<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:35-38`; `scripts/mcp_client.py:1465-1479` **Vulnerability Type**: Excessive authorization scope and unrestricted privileged tool invocation **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" ) ``` ```python 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 avatar workflow requires artifact upload and retrieval, model discovery, voice cloning or selection, speech generation, video generation, and task management. The requested credential also grants unrelated image generation, music generation, broad wallet spending, and access to shared task and artifact operations. The client accepts an arbitrary `tool_name` from the command line and forwards it to the authenticated MCP endpoint without a package-specific allowlist. Consequently, the broad bearer credential—not the declared Skill workflow—becomes the effective authorization boundary. The credential is shared across Beatra Skills, which further enlarges the blast radius. A local process or manipulated agent capable of invoking the bundled client can exercise every tool permitted by the token, including capabilities unrelated to founder-avatar generation. ### Attack Path 1. An attacker obtains the ability to influence commands executed in the Skill context, or another local process invokes the bundled client. 2. The attacker supplies an arbitrary MCP tool nam ...[truncated 882 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared full-scope credential with a package-specific credential restricted to the precise tools required by this workflow. 2. Remove unrelated scopes such as `images:generate` and `music:generate`. 3. Separate read-only wallet access from spending authorization and request spending capability only immediately before an explicitly approved billable operation. 4. Add a local allowlist for valid MCP tool names. Reject every tool not required by the documented workflow. 5. Apply server-side package and installation restrictions rather than trusting client-supplied source attribution. 6. Isolate artifacts and tasks by package or tenant so one Skill cannot inspect or cancel another Skill's work. 7. Require explicit confirmation for every operation that can spend credits or cancel work, even if the caller invokes the generic client directly. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Retrieval and Installation of Remotely Controlled Executable Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:515-522`, `scripts/mcp_client.py:969-1018`, and `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Automatic remote payload retrieval and package code replacement **Risk Level**: High ### Vulnerable Code ```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 ``` ```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) c ...[truncated 3215 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Permit automatic update checks, but require explicit informed approval before replacing executable files. 2. Verify every release with a cryptographic signature rooted in a publisher public key pinned in the reviewed package. 3. Prefer threshold signatures, offline release keys, or a transparent signed release log so compromise of one web service cannot authorize new code. 4. Bind the signed metadata to the package name, channel, locale, version, complete file manifest, and archive digest. 5. Display the current and proposed versions and obtain confirmation before installation. 6. Consider downloading updates into a separate staging area and requiring the hosting platform or package manager to install them. 7. Preserve the existing checksum, path-safety, ownership, locking, backup, and rollback controls as defense-in-depth. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:231
Finding
Server-Selected Upload URL Is Not Restricted to a Trusted Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:231-265` **Vulnerability Type**: Sensitive file upload to an insufficiently validated 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 upload grant is returned by the MCP service and controls the destination receivin ...[truncated 1803 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Maintain a strict allowlist of documented upload hostnames and reject all other destinations. 2. Match hostnames exactly or use carefully constrained suffix rules that cannot be bypassed by lookalike domains. 3. Reject literal IP addresses, user-information components, nonstandard schemes, and unapproved ports. 4. Require the MCP upload grant to be cryptographically signed and bind the signature to the destination URL, artifact identifier, MIME type, exact byte length, expiration time, and HTTP method. 5. Validate that the grant has a short expiration and is single-use. 6. Do not forward arbitrary server-provided headers. Allow only the minimal headers required by the approved storage provider. 7. Inform the user of the destination service before uploading sensitive biometric media. ]]>

other

Note
Location
scripts/authorize.py:338
Finding
Unnecessary Collection and Transmission of Host Identification Metadata<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:338-370` and `scripts/authorize.py:446-463` **Vulnerability Type**: Environment reconnaissance and installation fingerprinting **Risk Level**: Low ### 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] ``` ```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 = post_form(DEVICE_AUTHORIZATION_URL, form) ``` ### Technical Analysis Authorization reads environment signatures to identify the host agent and obtains the local h ...[truncated 1702 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not transmit the hostname by default. 2. Allow the user to choose a device label or explicitly opt into sharing the hostname. 3. Clearly disclose every telemetry field before authorization, including platform, package version, hostname, and stable installation reference. 4. Use a random, minimally scoped identifier that can be reset or rotated by the user. 5. Avoid retaining platform and device metadata longer than operationally necessary. 6. Provide a telemetry-disable option that does not prevent core authentication or media-generation functionality. 7. Document server-side retention, access, and deletion policies for device metadata. ]]>
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 (17)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documentation indicates capabilities equivalent to environment access, file read/write, network access, and shell execution, yet no permissions are explicitly declared. That creates a transparency and governance gap: users and policy engines cannot accurately assess or constrain what the skill can do before execution. In this context, those capabilities are especially sensitive because the skill also handles local media files, credentials, remote uploads, and package updates.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The advertised purpose is avatar-video generation, but the documented behavior includes OAuth/device authorization, credential storage, local file upload, installation registration/telemetry, self-update, and uninstall/state deletion. This mismatch is dangerous because it obscures materially different trust and security implications from users and defenders, making overbroad access easier to smuggle in under a benign-looking description.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill includes a self-updating package installer/runtime replacement mechanism that is not essential to the core avatar-generation task. Any code path that downloads and replaces local package files materially expands the attack surface: if the update channel, signing assumptions, or client integrity are compromised, an attacker can gain code execution or persist modified behavior under the guise of a content-generation tool.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest frames this as a founder-avatar skill, but the runtime behavior performs silent remote update checks and local file replacement. Even if the update logic claims verification, the hidden maintenance behavior changes the trust model from a static content tool to software that can modify itself, which is a meaningful security-relevant surprise.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The authorization helper detects agent platform identifiers from environment variables, reads the local hostname, and persists that metadata to host.json even though this is not required to obtain or use an OAuth device token. This creates unnecessary device fingerprinting and local telemetry about the host environment, which expands privacy exposure and can aid correlation of a user's machine, agent runtime, and account activity if the state directory is later accessed.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script records a local inventory of installed skills including slug, platform, and fully resolved install_path in ~/.beatra/skills.json. Storing absolute install paths is unrelated to the core media-generation purpose and leaks sensitive filesystem structure and usage history, which can reveal usernames, project names, mount points, and what other packages are installed if that file is accessed by another process or attacker.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The client contains a full self-update and package state-management subsystem that can download, validate, and replace installed package files, which is beyond the declared founder-avatar generation/upload purpose. Even with integrity checks, embedding self-modifying behavior in a skill materially expands the trust boundary and attack surface: compromise of the update channel, signing/discovery process, or backend control plane would let remote infrastructure rewrite local code.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The skill records local installation inventory and sends installation-registration telemetry unrelated to the narrowly stated avatar-studio task. This creates unnecessary data collection about local installs and usage context, which increases privacy risk and broadens the skill's operational scope without clear user need.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The code fingerprints the execution environment by checking agent-specific environment variables and host metadata, then transmits source_platform during tool calls and registration. For an avatar-generation skill, this is not obviously required and can be used to profile the user's tooling environment, increasing privacy and targeting risk if the backend or collected telemetry is abused.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that newer releases install automatically without separate confirmation. Silent installation of new code is risky because it bypasses informed consent and can introduce new permissions, behaviors, or malicious changes after the user initially trusted the package. In a skill that already has network, file, and shell-like operational reach, that risk is amplified.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The manifest explicitly advertises voice cloning and likeness-based video generation but includes no visible safety notice, consent validation requirement, or impersonation/privacy guardrails. In a skill centered on generating a founder or expert in their own likeness and voice, the absence of consent and misuse warnings increases the risk of deceptive impersonation, unauthorized biometric use, and privacy harm.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The document states that the client silently checks for and automatically installs newer releases by default without separate confirmation. Even though the text describes integrity checks and rollback protections, silent self-update with file replacement changes local software state without an explicit just-in-time warning or consent, which increases supply-chain and operational risk if the update channel is ever compromised or if the user is unaware of the behavior.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The helper writes host metadata to disk as a best-effort side effect without any user-facing notice, prompt, or documented consent path. Silent persistence of hostname and platform information undermines informed consent and increases the chance users unknowingly leave behind identifying metadata on shared or managed systems.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script silently records a local skill inventory with install paths and timestamps, creating a hidden audit trail of package usage and filesystem locations. Even if intended for uninstall coordination, writing this data without notice or consent is a privacy/security issue because it exposes operational metadata unrelated to the avatar function and may persist long after use.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
maybe_auto_update() performs a silent best-effort update path that can replace installed package files during normal execution, without prompting the user at the time of modification. Silent code replacement is dangerous because it changes the behavior of a locally installed skill outside the user's immediate awareness, and any compromise of the update authority would convert this into remote code deployment.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The client records local skill inventory and attempts registration telemetry as a best-effort background action without user-facing disclosure at execution time. While not direct code execution, undisclosed telemetry and local inventory persistence are risky because they collect and retain operational metadata outside the core avatar-generation function.

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
94% confidence
Finding
The exposed update command enables self-modification of the installed skill package, and the codebase also supports automated update application. Self-modifying agent skills are inherently higher risk because they permit post-installation behavior changes under remote control of the update service, which is especially concerning for a media-generation skill whose declared purpose does not require local code replacement.

Static analysis

No suspicious patterns detected.