Back to skill

Security audit

Wealth Product Talking Clips

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real Beatra media workflow, but it also grants broad shared account authority and silently self-updates local code, so it belongs in Review.

Install only if you are comfortable giving Beatra a broad shared device authorization, uploading selected factsheets/stills to Beatra, allowing the package to keep local state under ~/.beatra, and accepting default silent package updates. Consider disabling auto-update with the documented command and review Beatra account permissions, billing, and revocation options before use.

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)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/mcp_client.py:969
Finding
Default Silent Self-Update Allows Remote Replacement of Executable Skill Files Without Publisher Signature Verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mcp_client.py:31-32, 302-330, 469-491, 969-1019, 1543`; `SKILL.md:219-239`; `references/automatic-updates-and-safety.md:1-19` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code ```python PACKAGE_DISCOVERY_URL = "https://beatra.ai/skills/wealth-product-talking/channels/clawhub/install.json" PACKAGE_CDN_BASE_TEMPLATE = "https://cdn.beatra.ai/agent-packages/wealth-product-talking/channels/clawhub/v{version}" ``` The release URLs and hashes are accepted from a mutable discovery document: ```python def _release_urls(discovery: dict[str, Any]) -> tuple[str, str]: version = discovery.get("version") archive = discovery.get("archive") manifest = discovery.get("manifest") base_url = discovery.get("base_url") expected_base = PACKAGE_CDN_BASE_TEMPLATE.format(version=version) expected_archive = f"{PACKAGE_SLUG}-skill-{version}.zip" if ( discovery.get("schema_version") != 1 or discovery.get("package") != PACKAGE_SLUG or discovery.get("channel") != PACKAGE_CHANNEL or discovery.get("locale") != PACKAGE_LOCALE or not isinstance(version, str) or archive != expected_archive or manifest != "skill-manifest.json" or base_url != expected_base or not isinstance(discovery.get("archive_sha256"), str) or _SHA256.fullmatch(discovery["archive_sha256"]) is None or not isinstance(discovery.get("manifest_sha256"), str) or _SHA256.fullmatch(discovery["manifest_sha256"]) is None ): raise RuntimeError("Beatra update discovery is invalid") parsed = urllib.parse.urlsplit(expected_base) if ( parsed.scheme != "https" or parsed.hostname != "cdn.beatra.ai" or parsed.username is not None or parsed.password is not None or parsed.query or parsed.fragment ): raise RuntimeError("Beatra up ...[truncated 6791 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Disable automatic installation by default** - Default `auto_update` to `False`. - Permit automatic update checks, but require explicit user approval before replacing files. - Clearly display the current version, target version, affected files, and publisher identity. 2. **Add cryptographic publisher authentication** - Embed or securely provision an offline-controlled publisher public key. - Sign release metadata containing at least the package slug, channel, locale, version, expiry, manifest hash, archive hash, and file hashes. - Verify the signature locally before trusting any hash or release URL. - Reject expired, unsigned, incorrectly signed, or revoked metadata. 3. **Use a signed update framework** - Consider a design based on TUF or another framework supporting root-key rotation, metadata expiry, rollback resistance, and separation of repository roles. - Keep trusted root metadata independent from the mutable discovery service. 4. **Preserve existing hardening** - Retain redirect rejection, fixed-origin restrictions, downgrade prevention, archive traversal defenses, size limits, package ownership checks, locking, staging, rollback, and recovery journals. 5. **Protect sensitive client components** - Require stronger verification or explicit approval when replacing `scripts/mcp_client.py`, authorization code, or credential-handling code. - Report update success and the verified signer instead of performing completely silent executable replacement. 6. **Improve auditability** - Record the prior and new version, verified signer identity, manifest digest, update time, and changed files in a user-readable local log that excludes credentials and private content. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/authorize.py:34
Finding
Shared Full-Scope Device Token and Arbitrary Tool Dispatch Exceed the Skill's Required Privileges<![CDATA[ ## Vulnerability Details **File Location**: `scripts/authorize.py:34-36`; `scripts/mcp_client.py:1463-1479, 1486-1496`; `references/installation-and-auth.md:74`; `references/mcp-connection.md:8-10` **Vulnerability Type**: Excessive authorization scope and unrestricted MCP tool dispatch **Risk Level**: Medium ### Vulnerable Code The authorization helper requests one broad scope containing capabilities unrelated to this Skill's declared talking-video workflow: ```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 generic command accepts any tool name supplied through the command line and forwards it to the authenticated MCP service: ```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 CLI applies no package-specific tool allowlist: ```python call = subparsers.add_parser("call", help="Call one tool with a JSON object on stdin") call.add_argument("tool_name") ``` The documentation confirms that this is a shared, full-scope credential: ```text They share the one full-scope Device Token stored in `~/.beatra/credentials.json`. ``` ### Technical Analysis The declared workflow needs a limited set of operati ...[truncated 2891 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Issue package-specific credentials** - Avoid sharing one full-scope token among all Beatra Skills. - Bind each credential to the package slug and its approved tool set. - Ensure compromise of one Skill cannot authorize unrelated package operations. 2. **Reduce OAuth scopes** - Remove `images:generate` and `music:generate` from this Skill unless a documented workflow genuinely requires them. - Separate wallet reading from spending. - Separate task reading from task cancellation. - Grant voice-writing authority only when the user explicitly enables voice cloning. 3. **Enforce a local tool allowlist** - Restrict `call` to the exact tools required by this package, such as model/voice listing, authorized upload, voice cloning, speech synthesis, video animation, wallet reads, and task management. - Reject all other tool names before creating an authenticated session. - Maintain separate code paths for read-only, billable, and destructive calls. 4. **Add server-side package authorization** - Validate `source_package_slug` against token-bound permissions. - Do not treat source attribution as telemetry only. - Reject tools not authorized for the package even if a client attempts to call them. 5. **Use step-up authorization** - Require explicit, short-lived authorization for paid generation, voice cloning, or cancellation. - Bind approval to the operation type, estimated cost, relevant artifact identifiers, and an expiration time. 6. **Preserve user confirmation requirements** - Continue requiring separate confirmation cards for cloning, speech, and video. - Enforce those approvals technically on the server rather than relying solely on Agent instructions. ]]>
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill exposes substantial capabilities—shell, file access, environment access, and network use—without declaring permissions or surfacing them clearly to the user. That creates a trust and consent gap: a user may invoke what appears to be a simple media-generation skill while it can also inspect local files, persist data, and communicate externally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior goes well beyond the stated purpose of producing talking product clips: it includes OAuth login, persistent credential storage, arbitrary file upload via a generic client, telemetry/registration, uninstall cleanup, and automatic self-update. This mismatch is dangerous because users may authorize or provide data under a narrow mental model while the skill performs broader system and account-affecting actions.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest presents the skill as a local workflow that transforms user-supplied factsheets and stills into clips, but it is actually wired to a remote MCP endpoint over HTTP with bearer authentication. This creates a material trust-boundary mismatch: sensitive user documents, images, and generated content may be transmitted to an external service without the manifest making that data flow explicit, increasing privacy and exfiltration risk.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The authorization flow requests a very broad scope set, including wallet spending, music generation, and voice write access, which exceeds the stated purpose of generating talking clips from product factsheets and stills. If this credential is compromised or the skill is abused, the excess permissions enable materially broader actions than users would reasonably expect, increasing blast radius and financial risk.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
The script fingerprints the host agent environment and collects a recognizable device hostname, even though that information is not clearly necessary for producing talking clips. This creates unnecessary metadata collection that can expose user environment details, aid tracking across installations, or leak sensitive workstation naming conventions.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The script records a local inventory of installed skills and their install paths, which is unrelated to the narrow media-generation purpose and creates an unnecessary catalog of user tooling. That inventory can reveal filesystem layout, installed capabilities, and usage patterns to any process or actor that later accesses the state directory.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The client contains a full self-update channel, package discovery, archive download, installation mutation, and related state handling that go beyond the declared purpose of generating wealth-product talking clips. Even though there are integrity checks, this substantially expands the trust boundary: running the skill also grants a remote service the ability to replace local package code, which is dangerous for a media-generation skill because compromise of the update origin or misuse of this capability can lead to persistent code execution.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The code records local skill inventory and performs installation registration telemetry that are not necessary for creating talking clips from factsheets and stills. This creates unexpected collection and persistence of local metadata about installed skills and platform identity, which increases privacy risk and broadens the operational scope of the skill beyond its stated function.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The CLI exposes a generic tools/list and tools/call dispatcher that can invoke arbitrary remote MCP tools using whatever JSON object is provided on stdin, rather than restricting operations to the narrow clip-generation workflow. In the context of a specialized media skill, this effectively turns the package into a general-purpose remote capability launcher, increasing the chance of abuse, privilege expansion, and execution of unintended backend actions.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The uninstall script communicates with a remote authorization endpoint and manages shared device credentials, which is outside the narrow, user-facing purpose of generating wealth-product talking clips. Even if this is part of package lifecycle management, it introduces privileged account/device-management behavior that increases the attack surface and should be explicitly declared and tightly scoped.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
This code can revoke a shared device authorization and remove shared local state under ~/.beatra, affecting other installed skills if its inventory assumptions are wrong or manipulated. Because the skill’s advertised function is media generation, embedding shared credential destruction creates a high-impact hidden capability that can cause denial of service across unrelated skills and potentially sever platform access.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill states that the bundled client can automatically install newer releases without separate confirmation. Even with signature/manifest verification, silent self-update is a system-changing mechanism that can introduce new code, new behaviors, or supply-chain risk without an explicit approval step from the user at update time.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The document states that the client silently checks for updates and automatically installs newer versions without separate confirmation. Even though the text describes integrity checks and rollback protections, default silent modification of installed package files can surprise users and change local behavior without clear, prominent consent, which is a legitimate security and trust concern.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The flow requests high-impact permissions, including wallet spending and broad content-generation capabilities, but the user-facing output only instructs the user to select Allow without clearly explaining the scope or consequences. This undermines informed consent and makes over-privileged authorization more likely.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill can silently modify its own installed files during normal execution via maybe_auto_update(), without contemporaneous user approval or disclosure. For a skill whose stated purpose is media creation, hidden self-modification is especially risky because it enables persistent code changes outside the user's immediate awareness, making supply-chain compromise or unexpected behavior harder to detect.

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, demonstrating access to credential-bearing files unrelated to the skill’s core talking-clip function. In this context, credential access is dangerous because a content-generation skill should not need visibility into shared authentication material, and such access could be abused to disrupt service or expanded later into credential exfiltration.

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
94% confidence
Finding
The _device_token function reads an access token from credentials.json so it can perform remote revocation, giving this skill code direct access to shared authentication secrets. In the context of a wealth-product talking-clip skill, that is excessive privilege and materially increases the risk of credential misuse, service disruption, or future exfiltration if the package is modified or compromised.

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
96% confidence
Finding
The package explicitly supports self-modification through its self-update feature, allowing local code and files to be replaced after installation. Even with signature-by-hash style integrity checks, this is a powerful and risky capability in a narrowly scoped content-generation skill because any compromise of the update pipeline or mistaken update can produce persistent arbitrary behavior on the host.

Static analysis

No suspicious patterns detected.