Back to skill

Security audit

Booking Confirmation Voice Pack

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Beatra booking-voice workflow, but it grants broad account powers and silently updates package code beyond what the narrow voice-pack purpose needs.

Review this before installing. Use it only if you are comfortable authorizing a Beatra device token with broad media, artifact, wallet, voice, and task permissions, storing that shared token under ~/.beatra, sending package/platform/device metadata to Beatra, and allowing silent package-owned code updates unless you disable them with the provided update control.

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)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Remote Package Replacement Without an Independent Trust Root<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1019, 1542-1544`; related disclosure in `SKILL.md:178-183` **Vulnerability Type**: Silent retrieval and installation of remotely controlled executable content **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_up ...[truncated 2869 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default and require explicit, informed user approval before replacing package files. 2. Embed an offline publisher public key in the audited client and require a cryptographic signature over the release manifest. 3. Keep the signing key operationally separate from the discovery and CDN infrastructure. 4. Bind the signature to the package slug, version, channel, locale, complete file list, sizes, and hashes. 5. Display the proposed version, verified signer identity, changed executable files, and rollback information before installation. 6. Consider limiting automatic updates to non-executable data. Require additional approval for changes to `scripts/`, `SKILL.md`, or the updater itself. 7. Preserve the existing fixed-domain, redirect refusal, archive validation, ownership checks, locking, and rollback controls as defense-in-depth. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:31
Finding
Authorization Requests Privileges Beyond the Booking Voice Workflow<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:31-35`; related documentation at `references/installation-and-auth.md:73-75` **Vulnerability Type**: Excessive OAuth/device-token authorization scope **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" ) ``` This complete scope is requested during device authorization: ```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, } ``` ### Technical Analysis The declared Skill function is to create booking-confirmation voice clips. Its legitimate operations include speech synthesis, reading available voices and models, optional voice cloning and sample upload, reading relevant task results, and narrowly scoped billing queries. The requested token additionally authorizes image generation, video generation, music generation, broad artifact operations, wallet spending, and task cancellation. These capabilities are unrelated to producing booking voice clips and violate the principle of least privilege. The code also requires existing credentials to contain exactly this broad scope, so a narrower credential is rejected rather than used where possible. ### Attack Path 1. The user runs `scripts/authorize.py`. 2. The authorization flow requests the complete `SCOPE` value. 3. After approval, the broad bearer token is stored in `~/.beatra/credentials.json`. 4. A malicious prompt, compromised package update, local token thief, or operator error uses the token to invoke unrelated MCP capabilities. 5. The account may incur unrelated charges or have tasks and artifac ...[truncated 491 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a package-specific minimum scope covering only: - model and catalog-voice discovery; - speech synthesis; - optional voice cloning and sample upload when explicitly requested; - reads for tasks created by this Skill; - read-only wallet balance and ledger access when the user requests it. 2. Remove image, video, and music generation permissions. 3. Remove broad wallet-spending authority where operation-specific authorization is available. 4. Remove task cancellation unless the user separately requests cancellation and approves that capability. 5. Restrict artifact permissions to artifacts created or referenced by this package. 6. Use incremental authorization so cloning, upload, spending, or cancellation scopes are requested only when the relevant feature is used. 7. Accept valid narrower credentials for workflows that do not require optional capabilities. 8. Present the exact requested capabilities and their purpose on the approval page. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/mcp_client.py:1463
Finding
Arbitrary MCP Tool Dispatch Exposes the Full Token Capability Set<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1463-1482, 1489-1491` **Vulnerability Type**: Missing tool allowlist and operation-level authorization controls **Risk Level**: High ### Vulnerable Code ```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}, ) ``` The command-line parser accepts any tool name: ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` ### Technical Analysis The client performs no package-level allowlist validation before forwarding `tool_name` and arbitrary JSON arguments to `tools/call`. Server-side authorization remains present, but the bearer token itself has broad media, artifact, wallet, and task permissions. As a result, the local client is not constrained to the operations necessary for booking voice generation. This significantly amplifies the excessive-scope issue and creates a direct path for prompt injection, operator error, or malicious Skill instructions to invoke unrelated account operations. Using standard input for arguments appropriately avoids shell interpolation and command-line secret exposure; the defect is the absence of semantic authorization over the selected remote tool. ### Attack Path 1. The Skill or a manipulated ag ...[truncated 776 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a strict package-specific allowlist of MCP tool names in `mcp_client.py`. 2. Permit only the tools required by the documented workflow, such as relevant model and voice listing, speech synthesis, optional clone/upload, task reads, and narrowly required wallet reads. 3. Validate each tool’s argument schema locally before transmission. 4. Require a separate explicit confirmation for billable, destructive, cancellation, cloning, or upload operations. 5. Bind task reads and cancellations to task identifiers created by this package where the API supports it. 6. Reject unknown tools by default even if the remote server advertises them. 7. Combine the allowlist with narrower server-issued scopes; client-side checks must not be the sole authorization boundary. 8. Add tests proving that unrelated image, video, music, wallet-spend, artifact, and cancellation tools are rejected locally. ]]>

other

Warning
Location
scripts/authorize.py:336
Finding
Hostname and Agent Environment Telemetry Are Collected and Transmitted<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:336-370, 425-439, 502-505`; related attribution in `scripts/mcp_client.py:1149-1166, 1209-1218, 1354-1383` **Vulnerability Type**: Host environment telemetry beyond the minimum creative function **Risk Level**: Medium ### 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] ``` The collected hostname is included in the authorization request: ```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_AUTHORIZAT ...[truncated 2052 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make hostname and platform telemetry opt-in rather than collecting them automatically. 2. Clearly disclose each transmitted field before authorization, including hostname, platform, package version, and stable installation reference. 3. Use a random, user-editable device alias by default instead of the operating-system hostname. 4. Permit authorization and normal voice generation when telemetry fields are absent. 5. Avoid attaching platform attribution to every business call unless operationally necessary. 6. Define retention, deletion, and account-console controls for device telemetry. 7. Minimize local persistence and protect telemetry state with the same restrictive permissions used for other Beatra state files. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (22)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares no permissions while instructing use of a bundled Python client that can read local files, write/update package-owned files, access credentials, invoke shell commands, and make network calls. This creates a trust and review gap: a host may permit execution based on the manifest/description while the skill actually performs materially broader actions, including paid API calls and software update behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is presented as a narrow booking-confirmation voice generator, but the referenced behavior includes OAuth authorization, persistent credential handling, arbitrary file upload paths, telemetry/registration, uninstall/token revocation flows, and automatic self-updating. That mismatch is dangerous because users and reviewers may consent to a simple content-generation workflow without realizing they are also authorizing account linkage, local state changes, remote code/package changes, and data egress.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest presents the skill as a simple booking-confirmation voice generator, but it also declares an authenticated remote MCP endpoint. That mismatch creates a capability-expansion risk: users or host systems may grant networked access and credentials that are not clearly necessary for the advertised function, enabling undisclosed data access or remote actions.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
Authenticated remote MCP access is broader than the stated purpose of producing short booking voice clips from already confirmed facts. When a skill's declared purpose does not justify its remote authenticated capabilities, there is elevated risk of overcollection, unintended booking/customer data exposure, or hidden remote operations through the MCP channel.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The OAuth scope set is far broader than the skill's stated purpose of generating booking confirmation voice clips. It requests access to artifacts, images, videos, music, speech, voice management, task control, and other account capabilities that are not necessary for a narrow booking-confirmation workflow, violating least-privilege and materially increasing blast radius if the skill or credential is abused.

Context-Inappropriate Capability

Critical
Confidence
98% confidence
Finding
The scope list includes voice-management and task/artifact control permissions beyond a one-line audio clip generation use case. This could allow modifying voices, reading or writing artifacts, reading or canceling tasks, and otherwise affecting broader account state unrelated to simple booking confirmation output.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The scope list includes voice-management and task/artifact control permissions beyond a one-line audio clip generation use case. This could allow modifying voices, reading or writing artifacts, reading or canceling tasks, and otherwise affecting broader account state unrelated to simple booking confirmation output.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The scope list includes voice-management and task/artifact control permissions beyond a one-line audio clip generation use case. This could allow modifying voices, reading or writing artifacts, reading or canceling tasks, and otherwise affecting broader account state unrelated to simple booking confirmation output.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client does far more than the declared booking-confirm audio function: it can generically invoke remote tools, upload arbitrary files, self-update its own code, and send installation telemetry. That large remote-control surface materially expands what the skill can do after installation, making compromise of the remote service or misuse of the client much more dangerous than the stated purpose suggests.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The code fingerprints the host environment and records installation telemetry unrelated to generating booking confirmation voice clips. While not direct credential theft, this increases privacy risk and creates unnecessary data collection and outbound communication beyond user expectations for the advertised skill.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The package downloads manifests and archives from remote infrastructure and replaces files in its own installation tree. Even with integrity checks, self-modifying behavior is high risk for a simple voice-pack skill because it permits remote code changes post-install and turns server-side compromise into client-side code execution opportunity.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The uninstall script performs shared device-credential and connection lifecycle management that is materially outside the advertised purpose of a voice-clip generation skill. Even though framed as cleanup logic, code that can decide when to keep or revoke a shared authorization introduces privileged account-management behavior into a low-trust package, expanding blast radius if the skill is misused or modified.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
This section builds and sends an authenticated revocation request to the Beatra authorization service, giving the skill direct ability to affect shared installation state and device authorization. In the context of a simple booking-confirmation voice pack, embedding this capability is dangerous because compromise, repurposing, or user confusion could disconnect unrelated skills that rely on the same shared credential.

Vague Triggers

Medium
Confidence
78% confidence
Finding
The invocation wording is broad enough to capture generic booking-audio requests, which can cause the skill to activate in contexts the user did not intend. In this skill, mistaken routing is more concerning because activation can lead to remote service use, file handling, credentialed operations, and potentially paid actions, not just harmless text generation.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill describes automatic download and installation of newer package releases without a strong, proximate warning at execution boundaries. Even if signatures are verified, silent self-update expands the trusted codebase after approval and can introduce new capabilities or behavior without contemporaneous user review, which is especially sensitive in a skill that has network, file, credential, and paid-operation access.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The document states that the client silently checks for updates and automatically installs newer versions by default without separate confirmation. Even though it describes integrity checks and rollback protections, silently replacing executable/package files changes the user's environment without explicit consent and increases supply-chain risk if the trusted update source or signing/verification process is ever compromised.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The document states that the client automatically performs an installation registration call and stores a local registration cache, but it does not give an explicit user-facing warning or consent note about this telemetry-like transmission and persistence. Even though the data described appears limited and non-secret, silent collection and local storage of package, version, platform, and installation reference can create privacy, transparency, and compliance risks if users are unaware of it.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
maybe_auto_update() silently checks for and applies code updates during normal command execution, without a contemporaneous user-facing warning or consent prompt. Silent code replacement is risky because users may run a benign-looking booking command while the package modifies itself in the background.

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 authentication to a remote MCP service. Access to or use of host-stored credentials materially increases risk because a skill advertised for voice-pack generation could leverage those credentials to authenticate against remote infrastructure, potentially exposing account data or enabling unintended remote operations if the skill or server is compromised.

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 shared state files that include credentials.json shows the package is designed to interact with authentication material outside its functional scope. For a voice-pack skill, awareness of and cleanup authority over credential-bearing files increases the risk of accidental deletion, unauthorized access, or future abuse if the package is altered.

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 access_token from credentials.json and passes it into revocation logic, which is direct credential access. In a skill whose stated purpose is generating booking confirmation audio, this is unjustified privileged behavior and creates a path for token misuse, exfiltration, or denial of service against the shared device account if the code is changed or abused.

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 command surface explicitly exposes package self-update functionality, enabling the installed skill to replace its own code from remote sources. In the context of a narrowly described booking-confirm voice skill, self-modification is unjustified and significantly increases the blast radius of a repository, CDN, or update-channel compromise.

Static analysis

No suspicious patterns detected.