Back to skill

Security audit

Fund Dividend Talking Clips

Security checks for vulnerabilities and agentic risk

Overview

The skill’s clip-generation workflow is mostly coherent, but it needs Review because it uses a broad shared Beatra account token and silently replaces its own installed files by default.

Install only if you are comfortable granting Beatra a broad shared device authorization, storing that token under `~/.beatra`, uploading selected media to Beatra, and allowing this package to auto-update by default. Consider disabling automatic updates with `python3 scripts/mcp_client.py update --auto off`, reviewing Beatra account scopes, and revoking the device from the Beatra Console when no longer needed.

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:30
Finding
Overprivileged Shared Device Authorization Token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:30-34` **Vulnerability Type**: Excessive OAuth scopes and violation of least privilege **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" ) ``` ### Technical Analysis The Skill's documented workflow requires uploading selected media, generating speech, optionally cloning a voice, generating videos, discovering models, and reading relevant task results. The authorization request nevertheless asks for additional capabilities such as: - `images:generate` - `music:generate` - Broad `artifacts:read` - Broad `tasks:cancel` - General `wallet:spend` These permissions exceed the minimum privileges necessary for the declared fund-dividend talking-clip workflow. The resulting bearer credential is also shared among installed Beatra Skills through `~/.beatra/credentials.json`, increasing the impact of any compromised or malicious package with access to that credential. Although the Skill includes user-confirmation instructions before paid operations, these are agent-level procedural controls rather than enforcement at the credential or API authorization layer. Code possessing the token can bypass those instructions and invoke any granted operation directly. ### Attack Path 1. A malicious or compromised local process, Skill update, or other Beatra package gains execution under the same user. 2. It reads the shared bearer token from `~/.beatra/credentials.json`. 3. It sends authenticated requests to the fixed Beatra MCP endpoint. 4. It invokes unrelated capabilities covered by the broad scope, such as music or image generation, artifact access, task cancellation, or credit-consuming operations. 5. The requests execute under the victim's Beatra account because the server sees a valid full-scope token. ### ...[truncated 592 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a least-privilege scope specifically for this Skill. 2. Remove unrelated scopes such as `images:generate` and `music:generate`. 3. Restrict artifact access to artifacts created or explicitly selected for this workflow. 4. Restrict task cancellation to tasks created by the current package and installation. 5. Replace general wallet spending authority with server-enforced per-operation authorization. 6. Use package-specific credentials rather than one full-scope credential shared by every Beatra Skill. 7. Enforce paid-operation approval on the server side with short-lived, operation-specific grants. 8. Add automated tests that compare requested scopes against the capabilities declared in the Skill manifest. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Silent Remote Package Replacement Without Cryptographic Publisher Signatures<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:969-1018` **Vulnerability Type**: Automatic remote payload retrieval and executable package replacement **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( install_root=resolved_root, ...[truncated 3074 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic installation by default. 2. Require explicit, informed user confirmation before replacing executable package files. 3. Sign each release manifest with an offline publisher key. 4. Pin the corresponding verification public key in the reviewed client. 5. Verify the signature before trusting any version, URL, archive hash, manifest hash, or file hash. 6. Consider transparency-log verification and rollback protection tied to signed release metadata. 7. Separate update discovery infrastructure from release-signing authority. 8. Display the target version and verified signer identity before installation. 9. Retain the existing redirect, path, size, ownership, transaction, and rollback protections as defense in depth. 10. Consider making automatic checks read-only while requiring a separate explicit command to install an available update. ]]>

other

Note
Location
scripts/authorize.py:347
Finding
Unnecessary Hostname and Agent-Environment Telemetry<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:347-370`, `scripts/authorize.py:457-468` **Vulnerability Type**: Local environment reconnaissance and device metadata disclosure **Risk Level**: Low ### 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 values are 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 ``` ### Technical Analysis The authorization helper inspects se ...[truncated 1837 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not collect or transmit the real hostname by default. 2. Use a user-selected device label or a random non-identifying installation label. 3. Default agent-platform attribution to `unknown` unless the user explicitly opts in. 4. Display all metadata fields that will be transmitted before authorization begins. 5. Provide command-line options such as `--device-name` and `--no-telemetry`. 6. Minimize persisted host metadata and define a clear retention period. 7. Ensure declining telemetry does not prevent authentication or creative operations. 8. Document that platform attribution is attached to subsequent tool calls. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mcp_client.py:1044
Finding
Windows Credential Confidentiality Relies on Unverified Inherited ACLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:1044-1051` **Vulnerability Type**: Missing access-control validation for a sensitive bearer-token file **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") ``` The documented requirement is stricter than the implementation: ```text On Windows the current user must be the only principal granted access through the file ACL. ``` ### Technical Analysis On POSIX systems, the client validates all of the following before reading the bearer token: - The state path is a directory. - The directory is owned by the current user. - The directory mode is exactly `0700`. - The credential is a regular file. - The credential is owned by the current user. - The credential mode is exactly `0600`. - The file is opened with `O_NOFOLLOW` where supported. On Windows, the implementation performs none of the equivalent ownership or ACL checks. It assumes that the user-profile directory has safe inherited permissions and directly reads the credential. That assumption may be false when: - `~/.beatra` already exists with permissive ACLs. - Permissions were inherited from a customized or shared profile location. - The directory or file ACL was changed after authorization. - The home directory resolves to an administrator-managed or shared location. The token includes broad account capabilities, so local disclosure has a meaningful impact. ### Attack Path ...[truncated 1075 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `~/.beatra` and `credentials.json` with a restrictive Windows DACL. 2. Grant access only to the current user and, where unavoidable, required operating-system administrators. 3. Disable or carefully control inherited permissions on the credential file. 4. Validate the file owner, ACL entries, file type, and reparse-point status before every credential read. 5. Refuse to use the credential if unauthorized principals have read access. 6. Use supported Windows security APIs through a small, auditable platform-specific implementation rather than shell commands. 7. Revalidate permissions after atomic replacement of the credential file. 8. Add tests covering pre-existing permissive directories, shared profile paths, reparse points, and post-creation ACL modification. 9. Align documentation with actual enforcement and provide a safe recovery command that repairs permissions without printing or relocating the token. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill directs execution of a bundled Python client that reads local files, uploads assets, invokes networked services, stores credentials, and can update local package files, yet it declares no permissions. This is dangerous because users and host systems cannot accurately assess or constrain what the skill is allowed to access, creating hidden file, network, shell, and local state modification risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose presents a narrow media-generation workflow, but the skill also describes broad adjacent behaviors: OAuth/device auth, persistent bearer token storage, generic MCP invocation, telemetry/registration, uninstall/token revocation flows, local uploads, and self-update. That mismatch is dangerous because it conceals materially different trust and security implications from the user, increasing the chance of overbroad access, credential exposure, and unintended remote code or package changes under the guise of a simple content tool.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill states that the bundled client silently auto-updates and replaces package-owned files without separate confirmation. Even if downloads are described as verified and limited to official paths, silent code replacement materially changes the local execution surface after trust has been granted, which can introduce supply-chain risk and bypass expected review or approval controls.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The document explicitly states that the client silently checks for updates by default and automatically installs a newer release without separate confirmation. Even though it describes integrity checks and rollback protections, unattended code replacement changes the installed software behavior without an explicit per-update user approval, which creates a supply-chain and trust-boundary risk if the update infrastructure, signing/checking logic, or release process is ever compromised.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The document states that the client automatically performs an installation registration call and writes a local cache file, but it does not clearly warn users that metadata will be transmitted or that files will be created in their home directory. Even if the data is described as non-secret and non-billable, silent telemetry and persistent local writes can violate user expectations, privacy requirements, or enterprise policy when performed without explicit notice or consent.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The client performs silent automatic self-updates during normal command execution via maybe_auto_update(), which can replace package files without an interactive warning at execution time. Even though the update path includes signature-like hash validation and path-safety checks, any compromise of the trusted update origin, release pipeline, or discovery metadata would allow code replacement in the installed skill with little user visibility.

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
93% confidence
Finding
The package contains first-class self-modification capability through its update command and the auto-update flow, enabling it to replace its own installed files. In a skill context, self-modifying code materially increases supply-chain risk because future behavior can change after installation, and this file even invokes silent update checks during normal operations.

Static analysis

No suspicious patterns detected.