Back to skill

Security audit

Store PA Broadcast Pack

Security checks for vulnerabilities and agentic risk

Overview

The skill can create store PA audio, but it also grants broad Beatra account powers, stores a shared token, forwards arbitrary Beatra tool calls, and silently self-updates by default.

Install only if you are comfortable giving this package broad Beatra account access beyond PA audio, keeping a shared bearer token in ~/.beatra, sending device and installation metadata to Beatra, and allowing default silent package updates. Review the Beatra approval screen carefully, consider disabling auto-updates with the documented command, and revoke the device in the Beatra Console if you stop using it.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/authorize.py:34
Finding
Overprivileged OAuth Scope Combined with an Unrestricted Remote Tool Dispatcher<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-38`; `scripts/mcp_client.py:1463-1481` **Vulnerability Type**: Excessive authorization and unrestricted access to remote 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 bundled client then permits the caller to supply an arbitrary remote tool name: ```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 purpose of the Skill is to create store PA speech recordings, optionally including a consented voice clone and media upload. The requested authorization scope is substantially broader than that purpose. It includes image, video, and music generation, wallet spending, artifact access, and task cancellation. This violates least-privilege principles. The risk is amplified because `_run_command` does not enforce a package-specific allowlist. Any `tool_name` supplied on the command line is forwarded to Beatra using the shared bearer credential. Restrictions in `SKILL.md` are behavioral instructions rather than an executable authorization boundary and therefore ...[truncated 1328 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the full shared scope with a package-specific least-privilege grant limited to: - Text-to-speech generation. - Voice listing and explicitly approved voice cloning. - Required artifact upload operations. - Model and task-status reads. - Read-only wallet access only when requested. 2. Do not grant image, video, or music generation to this package. 3. Separate wallet spending and task cancellation into explicit, narrowly scoped grants requiring user confirmation. 4. Add a strict local allowlist in `_run_command`, such as the exact `beatra.*` tools documented by this Skill. 5. Reject unknown tool names before loading the credential or creating a network session. 6. Apply server-side package/tool authorization so client-side restrictions are not the only control. 7. Consider using short-lived, package-bound access tokens rather than one full-scope token shared by all Beatra Skills. ]]>

other

Warning
Location
scripts/authorize.py:362
Finding
Hostname Collection and Transmission Without Explicit Opt-In<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:362-369`; `scripts/authorize.py:450-468` **Vulnerability Type**: Device information collection and telemetry **Risk Level**: Medium ### Vulnerable Code ```python 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_AUTHORIZATION_URL, form) ``` ### Technical Analysis The helper obtains the machine hostname through `socket.gethostname()`, stores it in the local host configuration, and transmits it to `https://api.beatra.ai` as `device_name`. This is limited environment reconnaissance rather than broad system discovery: the code does not enumerate network interfaces, IP addresses, users, processes, or files. Nevertheless, the hostname is not necessary to authenticate the user or generate store PA recordings. Hostnames commonly contain employee names, employer identifiers, internal asset numbers, organizational naming conventions, or infrastructure roles. The project documents platform and installation registration telemetry, but the reviewed registration documentation does not clearly disclose that the actual local hostname is transmitted during authorization. ### Attack Path 1. The user runs `scripts/authorize.py`. 2. `authorize()` calls `device_display_name()`. 3. The helper reads the l ...[truncated 798 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not collect or transmit the operating-system hostname by default. 2. Use a generic non-sensitive label, such as `Store PA Broadcast Pack device`. 3. If a recognizable device label is desired, request explicit user consent and allow the user to choose the label. 4. Clearly disclose the exact data, destination, retention purpose, and correlation behavior before transmission. 5. Avoid storing the hostname in `host.json` unless it is operationally necessary. 6. Provide a configuration option that permanently disables device-name telemetry. 7. Minimize the authorization payload to fields strictly required by the OAuth device flow. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential File Is Read Without Verifying Its Access Control List<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1044-1052` **Vulnerability Type**: Insecure local bearer-token storage validation **Risk Level**: Medium ### Vulnerable Code ```python def _read_private_credentials(state_dir: Path, path: Path) -> str: if os.name == "nt": # The state directory lives under the user profile, whose default # ACL is already private to the user (the gh/aws/gcloud posture). # The former custom DACL verification was dropped deliberately: its # command patterns read as hostile to agent safety policies and # endpoint security, failing installs while adding nothing an # elevated administrator could not bypass. return path.read_text(encoding="utf-8") ``` ### Technical Analysis On POSIX systems, the client validates that the state directory and credential file are owned by the current user and have exact `0700` and `0600` permissions. The Windows branch applies no equivalent ownership or discretionary access control list validation before reading the bearer token. Relying on the user profile's default ACL is insufficient because ACLs can be changed, inherited from a permissive parent, preserved during migration, or exposed through shared and incorrectly configured profile directories. This also conflicts with the project's authentication documentation, which states that the current user must be the only principal granted access on Windows. Because the token carries broad paid and destructive capabilities, failure to verify its protection materially increases the effect of a local disclosure. ### Attack Path 1. The Skill is authorized on Windows and writes `credentials.json` under `~/.beatra`. 2. The directory or file inherits an ACL that permits another local principal to read it, or its ACL is later changed. 3. The client continues to accept and read the credential because the Windows branch performs no ACL validation. 4. Another local user or pro ...[truncated 775 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.beatra` and `credentials.json` with an explicit Windows DACL granting access only to the current user and required system principals. 2. Before each credential read, verify: - The file is a regular file and not a reparse point. - The current user owns the file. - No unexpected principal has read or write access. - The parent directory has equally restrictive permissions. 3. Refuse to use the credential when ACL verification fails and provide a safe repair procedure. 4. Use native Windows security APIs rather than shell commands to inspect and set security descriptors. 5. Write credentials atomically with restrictive security attributes in place at file creation time. 6. Add automated tests covering permissive inherited ACLs, changed ownership, reparse points, and migrated profile directories. 7. Reduce the token's scope so that any credential disclosure has a smaller financial and account-level impact. ]]>
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 (23)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares no permissions while its documented operation clearly requires filesystem access, network access, shell execution, environment use, and package modification behavior through the bundled client. This under-declaration prevents informed consent and weakens security review because users and hosting platforms cannot accurately assess the attack surface before installation or execution.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill is presented as a narrow content-generation tool, but the documentation shows materially broader behavior: OAuth login, token storage, arbitrary MCP tool invocation, local file upload, self-updating, registration/telemetry, and uninstall/revocation flows. That mismatch is dangerous because users may authorize a seemingly simple retail voice skill without realizing it can persist credentials, communicate externally, modify local files, and invoke broader remote capabilities.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The skill includes silent self-update and package replacement logic that is unrelated to producing PA announcement reads. Even with integrity checks described, automatic code replacement expands the trust boundary to remote update infrastructure and creates a supply-chain/system-modification risk during ordinary use.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
Ordinary commands trigger automatic update checks and potentially installations, which extends execution beyond the stated PA-generation purpose. This is risky because a content-generation action unexpectedly becomes a software-maintenance action, increasing exposure to remote infrastructure compromise, operational instability, and user surprise.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest presents the skill as a simple store PA copy/voice-pack builder, yet it is configured to contact a remote MCP endpoint using authenticated access. That creates a capability-to-purpose mismatch: users may grant networked, authenticated access that is not clearly necessary for the advertised function, increasing risk of undisclosed data access or remote actions.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
Using device-bearer authenticated remote service access for a skill whose visible purpose is generating PA announcement content is unjustified on its face. This expands the trust boundary beyond local content generation and could allow the service to access account-scoped resources or metadata unrelated to the user’s expectation.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
This helper requests a very broad OAuth scope set including artifacts, image/video/music/speech generation, voice read/write, wallet spending, and task control, which is far beyond what a store PA announcement pack would reasonably need. In the context of a narrowly described retail announcement skill, this creates excessive account provisioning and materially increases blast radius if the token or skill is abused.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The script fingerprints the host agent platform from environment variables and collects the device hostname, then sends this metadata during authorization and records it locally. For a skill advertised as building in-store PA announcements, this host/device inventorying is not obviously necessary and expands privacy and tracking exposure beyond the stated function.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The skill claims to generate store PA announcements, but the code exposes a general remote MCP client with tool invocation, file upload, telemetry, and package update capabilities. This is dangerous because it materially exceeds the declared business purpose, increasing the attack surface and enabling remote actions unrelated to PA content generation under the cover of an innocuous skill.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code fingerprints the host environment by inspecting environment variables and persisted host metadata, then transmits platform attribution with tool calls. For a store announcement generation skill, this collection is not obviously necessary and creates unnecessary privacy and reconnaissance value if the backend or package is abused.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill persists a local inventory of installed skills and performs installation registration telemetry to a remote service. That behavior is unrelated to composing store PA content and can reveal local software inventory and usage metadata, which broadens privacy and tracking risk beyond the advertised function.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The module presents itself as a minimal credential-backed HTTP client, but also implements self-updates, local inventory maintenance, platform detection, telemetry, and package replacement. This mismatch undermines transparency and makes security review and user consent less reliable, especially for a seemingly narrow-purpose retail content skill.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The uninstall flow can revoke a shared device authorization and remove global state under ~/.beatra, which affects more than this single skill package. Even though the code tries to preserve credentials when other skills remain, this capability is unrelated to the stated store-PA content function and creates a powerful side effect: if inventory is wrong, manipulated, or incomplete, uninstalling this package can disrupt other installed skills and remove shared credentials.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
This code performs device-authorization revocation against the Beatra API, a privileged account/device-management action that is unrelated to generating retail PA announcement content. Embedding revocation logic in a content-oriented skill expands the skill’s authority and enables denial of service against the user’s broader Beatra environment if triggered during uninstall or reused elsewhere.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation states that newer releases install without separate confirmation, but this system-changing behavior is not prominently surfaced as a primary warning before use. Silent installation undermines user consent and makes it easier for a compromised update channel or mistaken release to alter the local environment unexpectedly.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The file states that update checks are silent, enabled by default, and that higher versions are installed automatically without separate confirmation. Even with integrity checks and fixed update sources, silent auto-replacement of local package files reduces user awareness and consent, and can create security and operational risk if an update is flawed, unexpected, or changes behavior in a sensitive workflow.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The document states that the bundled client automatically performs an installation registration call and writes a local cache, but it does not provide an explicit user-facing warning or consent notice about the outbound metadata transmission and local persistence. Even though the data described is limited and non-secret, this creates a privacy and transparency issue because users may be unaware that package, version, platform, and installation reference data are being sent and stored automatically.

Missing User Warnings

Low
Confidence
70% confidence
Finding
The code writes host metadata such as platform and optionally device name to disk in host.json without an explicit user-facing warning or consent flow. While lower severity than token abuse, undisclosed persistence of host identity data creates avoidable privacy risk and can support local profiling of the user's environment.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code performs silent automatic self-updates that can replace installed package files during normal execution without interactive approval. Even with checksum validation and HTTPS, this creates a powerful remote code modification path that is especially risky for a skill whose stated purpose does not justify self-modifying behavior.

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
93% confidence
Finding
The script stores a long-lived bearer access token in a local credentials.json file after obtaining broad account permissions. Even though it uses private file permissions, compromise of the local account, backups, or developer tooling could expose a token capable of far more than this skill's advertised purpose.

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
90% confidence
Finding
The script explicitly targets credentials.json as part of the shared Beatra state it may delete, indicating awareness and handling of credential-bearing files outside the skill’s own data scope. In this skill context, access to shared credential storage is more dangerous because the package’s declared purpose does not require any credential lifecycle control, so compromise or misuse could affect the whole agent environment.

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
95% confidence
Finding
The _device_token function reads an access token from ~/.beatra/credentials.json in order to call the revoke endpoint. Reading and using a shared bearer token is sensitive credential access; if this skill or its execution context is abused, the token can be leveraged to revoke device authorization or potentially support other unauthorized API actions depending on token scope.

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 package includes a self-update mechanism capable of downloading and replacing its own installed files. Self-modification is inherently high risk because it can alter trusted local code after installation, and in this skill it is disproportionate to the declared purpose of generating store PA announcements.

Static analysis

No suspicious patterns detected.