Back to skill

Security audit

corporate-podcast-studio

Security checks for vulnerabilities and agentic risk

Overview

This skill is a legitimate podcast-generation integration, but it asks for broad account authority and silently self-updates executable package files by default.

Install only if you are comfortable giving this package a shared Beatra device credential with broad media, artifact, wallet, and task authority. Before using it, consider disabling silent updates with `python3 scripts/mcp_client.py update --auto off`, and only upload voice samples when you have clear speaker consent.

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

Warning
Location
scripts/authorize.py:31
Finding
Overbroad OAuth scopes combined with unrestricted MCP tool invocation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:31-35`; `scripts/mcp_client.py:1463-1481` **Vulnerability Type**: Violation of least privilege through excessive authorization and unrestricted tool forwarding **Risk Level**: Medium ### 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 client then permits an arbitrary MCP tool name to be supplied: ```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 Skill functionality is corporate podcast production using text-to-speech, catalog or cloned voices, artifact uploads, and asynchronous task handling. The authorization request nevertheless includes permissions for image, video, and music generation, in addition to wallet spending and task cancellation. These unrelated media-generation permissions are not necessary for the declared podcast workflow. Moreover, the generic `call` subcommand does not enforce a local allowlist of approved Beatra tools. Any caller able to invoke the bundled script can provide an arbitrary tool name and JSON arguments, which are forwarded to the MCP endpoint using ...[truncated 2184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reduce the requested OAuth scopes to those required by this Skill: - Speech generation. - Voice listing and, only when cloning is selected, voice writing. - Required artifact upload/read operations. - Model discovery and task polling. - Read-only wallet operations only when the user requests balance or ledger information. 2. Remove image, video, and music generation scopes from this package. 3. Separate sensitive capabilities into explicit, confirmation-gated grants: - Request voice-write access only for a user-approved cloning workflow. - Request task-cancellation authority only when cancellation is explicitly requested. - Avoid a general wallet-spending scope where the service supports narrower operation-specific grants. 4. Add a local allowlist in `_run_command`, rejecting any tool outside the documented podcast workflow. A suitable allowlist should be limited to the exact Beatra tool names required by `SKILL.md`. 5. Apply per-tool argument validation before forwarding requests, including rejecting unknown fields and requiring user confirmation for paid or destructive calls. 6. Do not share one full-scope credential among Skills with unrelated functions. Use package-specific or capability-specific credentials where supported. 7. Add automated tests proving that unrelated image, video, music, administrative, and unknown tools are rejected locally. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent default-enabled remote replacement of executable Skill files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1019`; automatic invocation at `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Mutable remote code delivery through automatic package updates **Risk Level**: High ### Vulnerable Code ```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_root, update_home=update_home, get_bytes=get_bytes, ) discovery = checked["discovery"] manifest, new_files = download_update(discovery, get_bytes=get_bytes) _apply_update( ...[truncated 4261 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make automatic installation disabled by default. Update checks may remain available, but replacing executable files should require explicit user approval. 2. Clearly display the current version, proposed version, source, and security-relevant changes before installation. 3. Sign every release manifest with an offline or otherwise independently protected publisher key. 4. Embed the corresponding public verification key in the audited client and reject updates with missing, invalid, expired, or untrusted signatures. 5. Use key rotation metadata that is itself signed by an already trusted key. 6. Keep the existing checksum, path, archive, ownership, downgrade, transaction, and rollback protections; digital signatures should supplement rather than replace them. 7. Consider delegating updates to the host marketplace or package manager so that installation policy, user consent, provenance, and rollback are enforced outside the Skill. 8. Pin an approved major version or release digest where operationally feasible, and require renewed approval for changes to executable scripts or authorization scopes. 9. Record successful update provenance locally, including the verified signing-key identity and release digest, to support incident response and reproducibility. 10. Treat update failure visibly when security verification fails. Silent fail-open behavior should not conceal signature or provenance errors from users or administrators. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (26)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documents use of a bundled Python client with shell execution, local file access, network access, and package-modifying behavior, yet it declares no permissions. That mismatch prevents users and policy systems from understanding the true execution surface and increases the chance of unexpected file, credential, and network operations occurring without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The advertised purpose is podcast generation, but the skill also performs authentication flows, persistent credential storage, remote tool invocation, local file upload, telemetry/registration, uninstall actions, and software update/install behavior. This is a substantial expansion of trust boundary and operational scope, making it easy for users to invoke a content-production skill without realizing it can alter the local environment and transmit sensitive local data.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The skill includes silent self-updating that can download, verify, and replace package-owned files automatically during ordinary use. Even with integrity checks described, silent code replacement is a dangerous capability in a content-generation skill because it changes executable behavior without a fresh trust decision from the user and expands the supply-chain attack surface.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest and top-level positioning present a podcast-production workflow, but the body documents automatic software maintenance and file replacement. This disconnect weakens informed consent and can cause users or orchestration systems to approve a low-risk media skill that actually performs system-modifying actions.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest markets a narrowly scoped podcast-production skill, yet it wires the skill to an authenticated remote MCP endpoint with no visible restriction or podcast-specific justification in this file. That mismatch creates a deceptive capability boundary: users may grant trust and credentials to a skill that can interact with a broader external service than its description implies.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
Authenticated access to a general remote MCP service is broader than what is needed for simple text-to-podcast generation based on the stated skill purpose. If the remote service exposes additional tools or data, the skill could access or relay information beyond user expectations, increasing the risk of data exposure or capability abuse.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The file documents a bundled client that silently checks for and installs software updates for Beatra packages, which is unrelated to a corporate podcast-generation skill. This kind of out-of-scope update mechanism increases the attack surface and suggests the skill may modify local software or fetch remote content without a business need tied to the declared functionality.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The documentation explicitly describes self-update capabilities, including downloading and replacing package files, for a skill whose purpose is podcast content generation. Even if integrity checks are described, a self-updating mechanism in this context is unjustified and dangerous because it enables remote code or file changes on the user's system outside the expected behavior of the skill.

Description-Behavior Mismatch

Medium
Confidence
85% confidence
Finding
The documentation describes an automatic installation-registration network call that is unrelated to the user-facing purpose of generating corporate podcasts. Even if marked non-billable and limited in scope, it collects and transmits package and environment metadata, creating an unnecessary data-flow and behavior-expansion surface for a content-generation skill.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
Telemetry-style installation registration is context-inappropriate for a corporate podcast studio because users would not reasonably expect outbound registration traffic from a creative content skill. This mismatch increases privacy and trust risk, especially since stable external installation references and platform data can support tracking across environments.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The authorization helper requests a very broad OAuth scope set, including wallet spending, task cancellation, artifact access, image/video/music generation, and voice read/write capabilities that exceed the stated purpose of a corporate podcast production skill. If the credential is compromised or the skill misuses it, the token grants materially expanded access and enables actions unrelated to podcast creation, violating least privilege.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The script detects the host agent platform from environment variables and captures a recognizable device hostname for persistence. While not code execution by itself, this collects and stores local identity/fingerprinting data beyond what a podcast-generation workflow appears to need, increasing privacy exposure and enabling environment profiling if the data is later transmitted or accessed by other components.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The script records a device-local inventory of installed skills, including installation paths and platform metadata, which is unrelated to the declared business purpose of producing executive podcasts. This creates unnecessary local surveillance of the user's environment and may expose sensitive filesystem layout or installed-tool information to other local processes or future code paths.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The client contains a full self-update mechanism that downloads manifests and archives, validates them, and replaces files under the installation root, which is well beyond the stated podcast-production purpose. Even with checksum and path validation, this introduces a remote code modification channel into a content-production skill, materially increasing supply-chain and post-installation risk if the update infrastructure or package publisher is compromised.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The skill records local inventory and sends installation registration telemetry unrelated to podcast generation, including package slug, version, platform, install path inventory, and external installation references. In the context of a corporate podcast skill, this extra persistence and telemetry broadens privacy and tracking exposure without being necessary for the user-facing function.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code fingerprints the host environment using environment variables and persisted host metadata to classify platforms such as Claude Code or Codex. For a corporate podcast creation skill, this capability is unrelated to producing audio content and can facilitate environment-aware behavior, tracking, or differentiated server-side handling that users may not expect.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The installation registration flow transmits package version, platform, and external installation reference as best-effort telemetry before ordinary tool use. In a skill advertised for podcast production, this behavior is unjustified by core functionality and creates avoidable metadata leakage about local deployments and usage context.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script manages shared device credentials and global skill inventory even though the skill is described as a podcast-production tool. That capability is outside the stated business purpose and gives this package authority over other installed skills' shared authentication state, which violates least privilege and creates an unnecessary account-impacting control surface.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
This code performs token-backed revocation against a central authorization endpoint and later coordinates deletion of shared local state. In the context of a podcast skill, that is an unjustified destructive capability: if triggered unexpectedly or packaged deceptively, it can disconnect the device and affect unrelated skills that rely on the same shared Beatra credential.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill discloses automatic update/install behavior, but not as a strong, pre-use warning for a system-modifying action. Users may invoke the skill for ordinary podcast generation without realizing it can trigger software maintenance and package replacement as part of routine execution.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The markdown states that the client silently checks for updates and automatically installs them without separate confirmation before ordinary commands. Silent system-modifying behavior undermines user consent and can lead to unexpected file replacement or execution-path changes, especially if the update channel is ever compromised or misconfigured.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The file documents outbound registration and creation of a local cache file but does not present an explicit user warning, consent prompt, or prominent disclosure. Silent data transmission and file creation can violate user expectations, create compliance issues, and make the behavior difficult for security reviewers and administrators to detect or govern.

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
95% confidence
Finding
The manifest explicitly references a local credential file for bearer-style authentication to a remote service. In the context of a content-generation skill, access to reusable credentials is especially sensitive because compromise or misuse could let the skill authenticate to external infrastructure under the user's identity and perform unintended actions.

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
93% confidence
Finding
Referencing credentials.json as part of the state this skill may remove indicates access to shared authentication material. Even if the script intends cleanup rather than exfiltration, a content-focused skill should not have direct reach into shared credential storage because compromise or misuse could invalidate auth for the whole device context.

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
96% confidence
Finding
The _device_token function reads an access token from credentials.json so it can call the revocation endpoint. Reading bearer tokens from shared local storage gives this package direct access to sensitive credentials, and in a non-authentication skill context that is excessive privilege that could be abused for denial of service or broader account actions if the token scope changes.

Static analysis

No suspicious patterns detected.