Back to skill

Security audit

Census Notice Voice Pack

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real Beatra voice-generation workflow, but it asks for broad account-level Beatra access and silently updates its own executable files beyond what a census voice-pack user would reasonably expect.

Review this before installing in a sensitive environment. Use it only if you are comfortable granting Beatra a broad shared device authorization, storing that token under ~/.beatra, allowing package telemetry, uploading any selected voice sample to Beatra, and accepting default silent package updates unless you immediately turn them off with the documented update --auto off command.

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:34
Finding
Overprivileged Shared Device Token and Unrestricted MCP Tool Dispatch<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-37`; `scripts/mcp_client.py:1463-1481` **Vulnerability Type**: Violation of least privilege and unrestricted privileged tool invocation **Risk Level**: High ### Code Snippets `scripts/authorize.py:34-37`: ```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" ) ``` `scripts/mcp_client.py:1463-1481`: ```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 this Skill is to turn census schedules into speech clips, with optional voice cloning and media upload. The authorization request nevertheless obtains permissions for unrelated image, video, and music generation, as well as wallet spending, voice modification, artifact access, and task cancellation. The resulting bearer credential is shared between Beatra Skills and is accepted by a generic MCP client. The `call` command accepts an arbitrary `tool_name` and forwards it directly to the remote service without a package-local allowlist or capability check. Consequently, the client itself does not constrain operations to those required by the census speech wor ...[truncated 1456 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared full-scope authorization request with the narrowest scopes required for: - Speech synthesis. - Required model and voice reads. - Explicitly requested voice cloning. - Explicit media upload. - Task creation and status reads. 2. Do not request image, video, music, wallet-spending, voice-writing, or task-cancellation privileges unless the current package genuinely requires them. 3. Introduce a strict client-side allowlist of MCP tool names appropriate to this Skill. 4. Separate read-only and paid operations into distinct authorization capabilities. 5. Require explicit user confirmation immediately before voice cloning, paid generation, cancellation, or any other state-changing operation. 6. Prefer package-bound or audience-bound tokens so one Skill cannot exercise privileges granted for unrelated Skills. 7. Validate the requested tool against both the package allowlist and the operation-specific input schema before sending it. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Unsigned Self-Update Replaces Executable Skill Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:31-32`, `scripts/mcp_client.py:969-1022`, `scripts/mcp_client.py:1542-1544` **Vulnerability Type**: Remote executable payload retrieval and automatic code replacement **Risk Level**: High ### Code Snippets `scripts/mcp_client.py:31-32`: ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/census-notice-voice/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/census-notice-voice/channels/clawhub/v{version}" ``` `scripts/mcp_client.py:969-1022`: ```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_ ...[truncated 3135 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. Silent checks may remain optional, but installation should require explicit user approval. 2. Sign release manifests with a dedicated offline or hardware-protected release key. 3. Embed or securely provision the corresponding public verification key independently of the update server. 4. Verify the signature before trusting version numbers, archive URLs, manifests, or hashes. 5. Apply key rotation through a signed trust-chain mechanism rather than through unsigned discovery metadata. 6. Display the current version, target version, changed files, and signing identity before installation. 7. Separate update checking from package replacement and provide a clear opt-in policy during initial setup. 8. Preserve the existing redirect, archive validation, path-safety, ownership, rollback, and resource-limit protections. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/authorize.py:339
Finding
Hostname and Agent-Environment Metadata Are Collected and Transmitted During Authorization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:339-380`, `scripts/authorize.py:444-467`, `scripts/authorize.py:566-568` **Vulnerability Type**: Environment reconnaissance and unnecessary metadata disclosure **Risk Level**: Medium ### Code Snippets `scripts/authorize.py:339-380`: ```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] def write_host_config(state_dir: Path, *, platform: str, device_name: str | None) -> None: """Persist detection results so mcp_client never re-detects per request and still has a truth source when its own env detection comes up empty. Best-effort: config failure must never block authorization.""" try: payload: dict[str, Any] = {"platform": platform} if device_name: payload["device_name"] = device_name ``` `scripts/authorize.py:456-4 ...[truncated 2595 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not collect or transmit the system hostname by default. 2. Offer an optional, user-selected device display name that does not expose the underlying hostname. 3. Make platform telemetry opt-in unless it is strictly necessary for protocol operation. 4. Clearly disclose every metadata field transmitted during authorization, its purpose, retention period, and deletion mechanism. 5. Avoid persisting hostname data in `host.json` unless the user explicitly enables this behavior. 6. If a device identifier is necessary, use a random opaque identifier rather than a human-readable host attribute. 7. Provide a command-line option such as `--device-name` and default to an anonymous label when it is omitted. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1045
Finding
Windows Credential Confidentiality Depends on Unverified Inherited ACLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:121-130`; `scripts/mcp_client.py:1045-1052` **Vulnerability Type**: Missing credential-file access-control enforcement **Risk Level**: Medium ### Code Snippets `scripts/authorize.py:121-130`: ```python def _private_directory(path: Path) -> None: # POSIX gets explicit 700/600. On Windows the state directory lives under # the user profile, whose default ACL is already private to the user — # the same posture as gh/aws/gcloud credential stores. The former custom # DACL ceremony was dropped deliberately: its command patterns read as # hostile to agent safety policies and endpoint security, failing installs # while adding no protection an elevated administrator could not bypass. path.mkdir(mode=0o700, parents=True, exist_ok=True) if os.name == "posix": path.chmod(0o700) ``` `scripts/mcp_client.py:1045-1052`: ```python 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 code explicitly enforces mode `0700` for the state directory and mode `0600` for credential files, verifies ownership, rejects unsafe permissions, and uses `O_NOFOLLOW` where available. On Windows, the code simply assumes that the user-profile directory has a private inherited ACL. It neither creates a restrictive discretionary access-control list nor validates the effective ACL before reading the bearer token. This conflicts with the documentation requirement that the current user be the only principal ...[truncated 1306 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the bearer token in Windows Credential Manager or protect it with DPAPI rather than relying on a plaintext JSON file. 2. If a file must be used, create an explicit ACL granting access only to the current user and required system principals. 3. Validate the file owner, inherited entries, and effective read permissions before loading the credential. 4. Reject credentials when broad groups such as `Users`, `Authenticated Users`, or `Everyone` have read access. 5. Apply equivalent ACL protection to temporary files and atomic-replacement targets. 6. Document the exact Windows protection model and keep implementation behavior consistent with the stated user-exclusive ACL requirement. 7. Retain the existing POSIX ownership, permission, symlink, and atomic-write controls. ]]>
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (28)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares no permissions while instructing use of file access, shell execution, networked MCP calls, credential handling, and local package modification via updates. This under-declaration prevents informed consent and weakens sandboxing or policy enforcement, especially because the skill can read local files, upload artifacts, and invoke remote operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose is narrow voice-clip generation, but the skill also performs authentication, credential storage, arbitrary tool invocation, local file upload, telemetry/registration, uninstall behavior, and self-updating installation logic. This mismatch is dangerous because users may authorize a seemingly simple content skill without realizing it introduces a general remote-integration client with persistent local state and code-changing behavior.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
Silent automatic self-updates materially change the installed package and execution behavior, yet this is not reflected in the manifest's simple voice-production description. Hidden self-modifying behavior increases supply-chain risk and undermines user expectations, especially in a skill that already executes local scripts and communicates over the network.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
Automatic package self-update is not necessary for the core task of generating voice clips and expands the trust boundary to remote code delivery. Even with verification claims, unnecessary updater functionality increases attack surface and can lead to unexpected code execution or persistence if the update channel is compromised.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The document describes a built-in client that silently self-updates and replaces local package files before ordinary commands, which is unrelated to a census voice-clip generation skill. Even with integrity checks, embedding autonomous update behavior into an unrelated skill expands the trust boundary and creates a supply-chain and persistence mechanism that could modify the local installation without task-specific justification.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
Silent update and file replacement capability gives the package authority to alter local code and assets without a prompt, which is disproportionate to the stated purpose of generating census notice audio clips. In this skill context, the capability is especially suspicious because it introduces a general software-modification channel where none is operationally required, increasing the risk of unauthorized changes or abuse if the update source or client is compromised.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The file is wholly focused on installing Beatra, obtaining a long-lived Device Token, and connecting to a remote MCP platform, which is unrelated to the declared census notice voice-clip purpose. That mismatch is a strong indicator of hidden capability expansion: a user expecting local census audio generation could instead be induced to authorize broad remote access and persistent credentials.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The instructions direct the agent to retain a long-lived Device Token and use it to access a broad remote MCP tool surface covering multiple media and task tools, far beyond the stated census clip generation function. This creates a persistent foothold and enables misuse of the user's authorization for unrelated remote operations if the skill or downstream service is abused.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The requested OAuth scope set is far broader than the skill’s stated purpose of turning written census schedules into speech clips. It includes unrelated capabilities such as artifact read/write, task control, image/video/music generation, voice management, and other account-level permissions, violating least privilege and creating unnecessary blast radius if the credential is abused or the skill is compromised.

Context-Inappropriate Capability

Critical
Confidence
98% confidence
Finding
The inclusion of voices:write allows modification of the user’s voice inventory, which is not obviously necessary for generating prewritten census notice clips. In this context, voice-management permissions could permit unauthorized creation, alteration, or replacement of voice assets, increasing both security and integrity risk.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The inclusion of voices:write allows modification of the user’s voice inventory, which is not obviously necessary for generating prewritten census notice clips. In this context, voice-management permissions could permit unauthorized creation, alteration, or replacement of voice assets, increasing both security and integrity risk.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The inclusion of voices:write allows modification of the user’s voice inventory, which is not obviously necessary for generating prewritten census notice clips. In this context, voice-management permissions could permit unauthorized creation, alteration, or replacement of voice assets, increasing both security and integrity risk.

Description-Behavior Mismatch

High
Confidence
94% confidence
Finding
The file implements a broad MCP client with credential use, remote tool invocation, upload, telemetry, and package update behavior that substantially exceeds the declared purpose of generating census notice voice clips. In a skill context, this overbreadth is dangerous because it gives the package a reusable control plane for arbitrary remote actions, increasing the blast radius if the remote service, package channel, or surrounding workflow is abused.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill can silently discover, download, validate, and apply package updates, including overwriting installation files, despite this capability being unrelated to its stated voice-pack purpose. Even with integrity checks, self-update expands trust to remote infrastructure and allows post-install behavior changes without task-time user review.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The code records local inventory and sends installation registration telemetry that is not necessary for converting census schedules into voice clips. Collecting and transmitting device-local metadata beyond the core function increases privacy risk and creates an unnecessary external dependency.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The CLI exposes generic remote tool enumeration and arbitrary tool invocation, allowing the package to act as a general-purpose front end to the Beatra MCP service rather than a constrained census voice tool. In this context, that mismatch is especially risky because users may install it expecting a narrow media workflow while it can trigger unrelated remote capabilities.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This uninstall script is explicitly designed to revoke a shared Beatra device authorization and remove global state under ~/.beatra, which exceeds the scope of a census voice-clip skill. Even if framed as cleanup logic, embedding cross-skill credential and shared state management inside a content-generation skill creates a dangerous capability that can disable unrelated installed skills and affect the user's account linkage.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code can issue an authenticated POST to revoke the device token against the Beatra authorization service, giving this skill the ability to remotely invalidate shared device access. For an audio-generation skill, this is an unnecessary privileged operation and could be abused to deny service to other skills or sever the user's connection unexpectedly.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The script defines deletion of shared files including credentials.json, installation.json, host.json, skills.json, and registrations.json under ~/.beatra, which are clearly broader than this skill's own data. This creates a destructive local capability that can remove credentials and platform inventory for all skills, causing loss of access or inconsistent platform state.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill states that newer versions install automatically without separate confirmation, but the upfront description does not clearly warn users about this system-modifying behavior. Silent installs reduce informed consent and can enable unreviewed code changes on the host, which is particularly risky for a package that can invoke shell commands and network services.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The file states that update checks are silent and enabled by default, and that newer versions install automatically without separate confirmation. Default-on background replacement of local installation files reduces user awareness and control, making unexpected code changes more likely to go unnoticed and harder to audit.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The documentation states that the bundled client automatically performs an installation registration call on first use and writes a local cache file, but it does not clearly warn the user at the point of use that metadata will be transmitted externally and persisted on disk. Even if the transmitted fields are described as non-secret, silent outbound registration and filesystem modification can violate user expectations, create privacy/compliance issues, and be unsafe in locked-down or sensitive environments.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code performs best-effort silent auto-update on normal execution, making network requests and potentially replacing local package files without a contemporaneous user-facing prompt. That creates a supply-chain and transparency risk because behavior can change during ordinary use outside the expected voice-generation workflow.

Credential Access

High
Category
Privilege Escalation
Content
- `~/.beatra/installation.json` contains one stable, non-secret installation
  reference.
- `~/.beatra/credentials.json` contains the single Device Token.

On POSIX systems the directory must be mode `0700` and both files mode `0600`.
On Windows the current user must be the only principal granted access through
Confidence
89% confidence
Finding
This line documents storage of a persistent Device Token in a local credentials file, which is sensitive credential material. Although the file permissions guidance is reasonable, embedding credential acquisition and retention inside a skill that does not appear to need such access expands the attack surface and normalizes local secret storage for an unrelated purpose.

Credential Access

High
Category
Privilege Escalation
Content
4. polls every 5 seconds for up to 15 minutes while the user signs in (or
   creates their account) and selects Allow;
5. atomically saves the returned Device Token to
   `~/.beatra/credentials.json` without printing an HTTP response body;
6. validates the new credential with the same non-billable MCP request and
   prints Ready only after it succeeds.
Confidence
90% confidence
Finding
The instruction to save the returned Device Token to a local credentials file confirms that the skill persists reusable authentication material on disk. Persistent bearer-token storage increases the risk of credential theft or later abuse, especially because the associated remote platform access is broader than the skill's stated census-audio purpose.

Static analysis

No suspicious patterns detected.