Back to skill

Security audit

Radio Drama Ad Breaks

Security checks for vulnerabilities and agentic risk

Overview

This audio-generation skill is disclosed, but it asks for broad Beatra account authority, exposes a generic remote tool caller, and silently self-updates local package code by default.

Review this before installing. It is not just a narrow text-to-speech helper: it creates a shared Beatra authorization under `~/.beatra`, can spend Beatra credits through a broad token, can invoke arbitrary exposed Beatra MCP tools through its bundled client, registers installation metadata, and silently applies package updates unless disabled with `python3 scripts/mcp_client.py update --auto off`. Use it only if you trust Beatra's account, update, and credit controls for this machine, and revoke the device from the Beatra Console if you stop using 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
  • 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:30
Finding
Overprivileged Device Token Combined with Unrestricted MCP Tool Invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:30-34`; additionally exploitable through `scripts/mcp_client.py:1460-1479` and `scripts/mcp_client.py:1497-1499` **Vulnerability Type**: Violation of least privilege and unrestricted access to remotely exposed MCP tools **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 generic command handler accepts any tool name supplied on the command line and forwards arbitrary JSON arguments: ```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 declared purpose of this Skill is to create radio-drama bumper audio, optionally clone an authorized voice, upload a sample, inspect models and voices, monitor tasks, and query billing information. The authorization request nevertheless obtains permissions for unrelated image, video, and music generation, as well as broad artifact, task-cancellation, and wallet-spending capabilities. This violates least privilege ...[truncated 2403 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Request only capabilities required by this Skill: - Speech generation. - Voice listing. - Voice creation only when cloning is explicitly enabled. - Artifact upload/read only when an authorized local sample is used. - Task read and narrowly scoped cancellation. - Read-only wallet access where required. 2. Remove image, video, and music generation scopes from this package. 3. Avoid a general wallet-spending scope where the service can instead authorize only explicit Skill operations. 4. Replace the unrestricted tool dispatcher with a package-specific allowlist, for example: - `beatra.models.list` - `beatra.voices.list` - `beatra.voices.clone` - `beatra.speech.synthesize` - `beatra.assets.upload` - `beatra.tasks.get` - `beatra.tasks.list` - `beatra.tasks.cancel` - `beatra.wallet.get` - `beatra.wallet.ledger` - `beatra.installations.register` 5. Reject every tool not present in the allowlist before creating an MCP request. 6. Where supported, issue a separate short-lived grant for optional voice cloning or uploads rather than permanently including those permissions. 7. Bind authorization server-side to the package identity and enforce the same tool allowlist remotely, so bypassing the local client does not restore excessive access. 8. Require explicit user confirmation immediately before operations that spend credits or cancel tasks. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Default-On Remote Code Replacement Without an Independently Authenticated Signature<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1009`; related defaults and invocation at `scripts/mcp_client.py:520-522` and `scripts/mcp_client.py:1539-1544` **Vulnerability Type**: Automatic remote payload retrieval and local code replacement **Risk Level**: High ### Vulnerable Code Invalid or absent update state enables automatic updates by default: ```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 ``` Ordinary MCP operations silently check, download, and apply a higher remote 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 ...[truncated 4537 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Update checks may remain opt-in, but code replacement should require explicit user approval. 2. Separate update discovery from update installation: - Report the available version. - Show the source and release metadata. - Require confirmation before downloading or applying executable files. 3. Sign release manifests with an offline or hardware-protected publisher key. 4. Embed or securely provision the corresponding public verification key in the reviewed package. 5. Verify a detached signature over the package identity, channel, locale, version, archive hash, and complete file manifest before replacement. 6. Consider threshold signatures or a trusted update framework such as TUF to reduce single-key and rollback risks. 7. Preserve the existing HTTPS restrictions, archive limits, path validation, ownership checks, and transactional rollback; these remain valuable defense-in-depth measures. 8. Avoid silently swallowing all update exceptions. Record a non-sensitive local diagnostic or return a clear warning without interrupting the requested business operation. 9. Require confirmation again if an update modifies executable files or the primary Skill instruction file. 10. Provide a documented immutable-version mode for environments that require reproducible, reviewable Skill deployments. ]]>
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 (22)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill exposes broad capabilities including shell, network, file read/write, and environment access without declaring permissions or constraining them to the minimum needed for its advertised task. This creates a large hidden trust boundary: users are asked to run a bundled client that can access local state and remote services, making misuse, credential exposure, or unintended system interaction harder to assess and control.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior substantially exceeds the claimed purpose of generating bumper audio, adding authentication flows, credential storage, local file upload, remote tool invocation, telemetry/registration, uninstall logic, and self-updating package behavior. This mismatch is dangerous because users may consent to a simple media-generation skill while unknowingly granting a much more privileged agent broad access to their machine, files, and accounts.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
Including bundled-client auto-update behavior inside a narrowly scoped audio-production skill expands the attack surface beyond the business function into software lifecycle management. Any updater logic that downloads and replaces local package files can become a supply-chain or local integrity risk if compromised, misconfigured, or insufficiently visible to the user.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
Silent automatic updates are not necessary for the stated task of producing bumper clips and introduce a persistent code-execution path unrelated to the user’s requested output. Even if the update channel is intended to be verified, the ability to download and self-replace code without separate approval materially increases supply-chain and post-install compromise risk.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The manifest presents the skill as a radio-drama audio bumper generator, but the changelog references unrelated balance, ledger, top-up, and address behavior. This capability mismatch is a strong indicator of hidden financial functionality or repurposed code, which can mislead users into authorizing access they would not expect from an audio-production skill.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
Balance and ledger access are not justified by the stated function of creating radio-drama ad-break voiceovers. Even if described as read-only, financial account visibility can expose sensitive transactional data and may be a stepping stone for social engineering, profiling, or later abuse of authenticated account context.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The authorization helper provisions a shared OAuth credential for a narrowly described radio-drama bumper skill, but the script is designed as a general Beatra account enrollment flow rather than a least-privilege, task-specific permission grant. In this skill context, bundling broad account authorization logic increases the blast radius if the credential is abused, because compromise of the skill or its state directory could expose access beyond the stated bumper-generation function.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The requested OAuth scope includes wallet spending, image generation, video generation, music generation, task cancellation, voice management, and broad artifact/task access, which far exceeds the stated purpose of producing radio-drama bumper voiceovers. Over-scoped tokens violate least privilege and could enable financial abuse, unauthorized content generation, or broader account actions if the credential is stolen or the skill behaves unexpectedly.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The client implements a full remote self-update system that downloads manifests and archives and then replaces local package files on disk. Even with several integrity checks, this capability materially exceeds the skill's stated radio-bumper generation purpose and creates a code-replacement channel that can change local behavior after installation if the update origin, signing pipeline, or server-side release process is compromised.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code detects host environment details, records local inventory, and transmits installation/platform registration telemetry that is not necessary for converting ad-break text into audio bumpers. In a narrowly scoped creative skill, this surplus collection increases privacy and tracking risk and broadens the trust boundary without delivering the declared function.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The CLI exposes a generic remote tool broker via 'tools' and 'call', allowing arbitrary backend tool listing and invocation rather than a narrowly constrained bumper-generation workflow. That makes the installed skill a general-purpose remote action client, which is much more powerful than its manifest suggests and could be abused to invoke unrelated capabilities through the shared MCP service.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The uninstall script performs privileged shared-state management and device-token revocation that are unrelated to the advertised radio-drama audio generation function. Even if intended as lifecycle cleanup, it gives this skill access to cross-skill authorization state and the ability to affect other installed skills by revoking the shared device credential.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
This code deletes files in the shared ~/.beatra state directory, including credentials and inventory used by all installed skills. Although it tries to preserve the connection when other skills remain, any bug, corrupted inventory, or environmental edge case could remove shared state and disrupt unrelated skills; the capability itself is over-broad for this skill's purpose.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The script sends the device bearer token to a remote revocation endpoint during uninstall, a network-side credential-management action outside the narrow audio-generation scope of the skill. While the endpoint is fixed and uses HTTPS, embedding revocation logic in the skill expands the skill's authority over shared authentication and creates denial-of-service risk if triggered improperly.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill allows automatic installation of newer package versions without separate confirmation, but this system-modifying behavior is not clearly disclosed in the front-facing description. Hidden or under-disclosed self-modification undermines informed consent and can surprise users or administrators with network activity and local file replacement they did not reasonably expect from an audio-generation skill.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The document states that the client silently checks for updates and automatically installs them by default, which modifies the local installation without an explicit foreground warning or opt-in at the point of use. Even with integrity checks and rollback protections, default silent self-modification can surprise users, violate change-control expectations, and create risk in production or regulated environments if behavior changes unexpectedly.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation describes automatic installation registration that sends package, version, platform, and external installation reference on first use, but it does not clearly warn users about the telemetry/privacy implications or require explicit opt-in. Even if the data is described as non-secret and non-billable, silent transmission of environment metadata can violate user expectations, create compliance issues, and expose identifiable deployment information.

Credential Access

High
Category
Privilege Escalation
Content
},
  "mcp": {
    "authentication": "device-bearer",
    "credential_file": "~/.beatra/credentials.json",
    "name": "beatra",
    "transport": "streamable-http",
    "url": "https://mcp.beatra.ai/mcp"
Confidence
82% confidence
Finding
The manifest explicitly references a local credential file used for device-bearer authentication, meaning the skill depends on reusable bearer credentials to access a remote MCP service. In the context of a simple audio bumper skill, this increases risk because compromise, overbroad use, or undocumented server-side actions could expose user account access beyond what the skill description suggests.

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
83% confidence
Finding
The script stores a long-lived bearer access token in plaintext JSON under the user's home directory. Although it attempts to set 0600 permissions, plaintext filesystem storage remains sensitive, especially because the token carries overly broad scopes; any local compromise, backup leak, or unsafe file sharing could expose full account capabilities.

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
89% confidence
Finding
Referencing credentials.json as removable shared state indicates the skill is designed to handle and potentially delete stored authentication material. In this context, credential access is sensitive because the credential is shared across skills, so compromise or misuse can affect all skills on the device rather than only this package.

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
94% confidence
Finding
The _device_token function reads an access token from credentials.json so the skill can use it for revocation. Reading raw bearer tokens from disk within a content-focused skill unnecessarily grants credential-handling capability; if the script or package is modified, that same access path could exfiltrate tokens or misuse them for unauthorized API actions.

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
96% confidence
Finding
A self-update command that can fetch and replace local package files is a self-modification capability, which is especially risky in a skill whose declared role is just producing bumper audio. This enables post-installation behavior changes and persistence-like modification of executable content, increasing the blast radius of any upstream compromise or policy bypass.

Static analysis

No suspicious patterns detected.